diff --git a/.bazelrc b/.bazelrc index 9a58b60c3ef..a304a7b0e1a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -1,3 +1,3 @@ -build --repo_env=CC=clang --repo_env=CXX=clang++ --copt="-std=c++17" +build --repo_env=CC=clang --repo_env=CXX=clang++ --cxxopt="-std=c++17" try-import %workspace%/local.bazelrc diff --git a/cpp/ql/lib/change-notes/2022-09-12-uppercase.md b/cpp/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/cpp/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow.qll new file mode 100644 index 00000000000..acb2fbc6808 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow.qll @@ -0,0 +1,26 @@ +/** + * Provides a library for local (intra-procedural) and global (inter-procedural) + * data flow analysis: deciding whether data can flow from a _source_ to a + * _sink_. This library differs from the one in `semmle.code.cpp.dataflow` in that + * this library uses the IR (Intermediate Representation) library, which provides + * a more precise semantic representation of the program, whereas the other dataflow + * library uses the more syntax-oriented ASTs. This library should provide more accurate + * results than the AST-based library in most scenarios. + * + * Unless configured otherwise, _flow_ means that the exact value of + * the source may reach the sink. We do not track flow across pointer + * dereferences or array indexing. + * + * To use global (interprocedural) data flow, extend the class + * `DataFlow::Configuration` as documented on that class. To use local + * (intraprocedural) data flow between expressions, call + * `DataFlow::localExprFlow`. For more general cases of local data flow, call + * `DataFlow::localFlow` or `DataFlow::localFlowStep` with arguments of type + * `DataFlow::Node`. + */ + +import cpp + +module DataFlow { + import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow2.qll new file mode 100644 index 00000000000..0513bd4ebcb --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow2.qll @@ -0,0 +1,16 @@ +/** + * Provides a `DataFlow2` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation. + */ + +import cpp + +module DataFlow2 { + import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl2 +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow3.qll new file mode 100644 index 00000000000..571b75b37c4 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow3.qll @@ -0,0 +1,16 @@ +/** + * Provides a `DataFlow3` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation. + */ + +import cpp + +module DataFlow3 { + import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl3 +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow4.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow4.qll new file mode 100644 index 00000000000..18c21e554f6 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/DataFlow4.qll @@ -0,0 +1,16 @@ +/** + * Provides a `DataFlow4` module, which is a copy of the `DataFlow` module. Use + * this class when data-flow configurations must depend on each other. Two + * classes extending `DataFlow::Configuration` should never depend on each + * other, but one of them should instead depend on a + * `DataFlow2::Configuration`, a `DataFlow3::Configuration`, or a + * `DataFlow4::Configuration`. + * + * See `semmle.code.cpp.ir.dataflow.DataFlow` for the full documentation. + */ + +import cpp + +module DataFlow4 { + import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImpl4 +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/ResolveCall.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/ResolveCall.qll new file mode 100644 index 00000000000..bcf2fa8c7db --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/ResolveCall.qll @@ -0,0 +1,23 @@ +/** + * Provides a predicate for non-contextual virtual dispatch and function + * pointer resolution. + */ + +import cpp +private import semmle.code.cpp.ir.ValueNumbering +private import internal.DataFlowDispatch +private import semmle.code.cpp.ir.IR + +/** + * Resolve potential target function(s) for `call`. + * + * If `call` is a call through a function pointer (`ExprCall`) or its target is + * a virtual member function, simple data flow analysis is performed in order + * to identify the possible target(s). + */ +Function resolveCall(Call call) { + exists(CallInstruction callInstruction | + callInstruction.getAst() = call and + result = viableCallable(callInstruction) + ) +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking.qll new file mode 100644 index 00000000000..43064116499 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking.qll @@ -0,0 +1,23 @@ +/** + * Provides classes for performing local (intra-procedural) and + * global (inter-procedural) taint-tracking analyses. + * + * We define _taint propagation_ informally to mean that a substantial part of + * the information from the source is preserved at the sink. For example, taint + * propagates from `x` to `x + 100`, but it does not propagate from `x` to `x > + * 100` since we consider a single bit of information to be too little. + * + * To use global (interprocedural) taint tracking, extend the class + * `TaintTracking::Configuration` as documented on that class. To use local + * (intraprocedural) taint tracking between expressions, call + * `TaintTracking::localExprTaint`. For more general cases of local taint + * tracking, call `TaintTracking::localTaint` or + * `TaintTracking::localTaintStep` with arguments of type `DataFlow::Node`. + */ + +import semmle.code.cpp.ir.dataflow.DataFlow +import semmle.code.cpp.ir.dataflow.DataFlow2 + +module TaintTracking { + import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking1.TaintTrackingImpl +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking2.qll new file mode 100644 index 00000000000..aa9fd392803 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking2.qll @@ -0,0 +1,15 @@ +/** + * Provides a `TaintTracking2` module, which is a copy of the `TaintTracking` + * module. Use this class when data-flow configurations or taint-tracking + * configurations must depend on each other. Two classes extending + * `DataFlow::Configuration` should never depend on each other, but one of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The + * `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and + * `TaintTracking2::Configuration` extends `DataFlow2::Configuration`. + * + * See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation. + */ +module TaintTracking2 { + import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking2.TaintTrackingImpl +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking3.qll new file mode 100644 index 00000000000..bcd35ccd02f --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/TaintTracking3.qll @@ -0,0 +1,15 @@ +/** + * Provides a `TaintTracking3` module, which is a copy of the `TaintTracking` + * module. Use this class when data-flow configurations or taint-tracking + * configurations must depend on each other. Two classes extending + * `DataFlow::Configuration` should never depend on each other, but one of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. The + * `TaintTracking::Configuration` class extends `DataFlow::Configuration`, and + * `TaintTracking2::Configuration` extends `DataFlow2::Configuration`. + * + * See `semmle.code.cpp.ir.dataflow.TaintTracking` for the full documentation. + */ +module TaintTracking3 { + import experimental.semmle.code.cpp.ir.dataflow.internal.tainttracking3.TaintTrackingImpl +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll new file mode 100644 index 00000000000..f92612e77af --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowDispatch.qll @@ -0,0 +1,273 @@ +private import cpp +private import semmle.code.cpp.ir.IR +private import experimental.semmle.code.cpp.ir.dataflow.DataFlow +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import DataFlowImplCommon as DataFlowImplCommon + +/** + * Gets a function that might be called by `call`. + */ +cached +Function viableCallable(CallInstruction call) { + DataFlowImplCommon::forceCachingInSameStage() and + result = call.getStaticCallTarget() + or + // If the target of the call does not have a body in the snapshot, it might + // be because the target is just a header declaration, and the real target + // will be determined at run time when the caller and callee are linked + // together by the operating system's dynamic linker. In case a _unique_ + // function with the right signature is present in the database, we return + // that as a potential callee. + exists(string qualifiedName, int nparams | + callSignatureWithoutBody(qualifiedName, nparams, call) and + functionSignatureWithBody(qualifiedName, nparams, result) and + strictcount(Function other | functionSignatureWithBody(qualifiedName, nparams, other)) = 1 + ) + or + // Virtual dispatch + result = call.(VirtualDispatch::DataSensitiveCall).resolve() +} + +/** + * Provides virtual dispatch support compatible with the original + * implementation of `semmle.code.cpp.security.TaintTracking`. + */ +private module VirtualDispatch { + /** A call that may dispatch differently depending on the qualifier value. */ + abstract class DataSensitiveCall extends DataFlowCall { + /** + * Gets the node whose value determines the target of this call. This node + * could be the qualifier of a virtual dispatch or the function-pointer + * expression in a call to a function pointer. What they have in common is + * that we need to find out which data flows there, and then it's up to the + * `resolve` predicate to stitch that information together and resolve the + * call. + */ + abstract DataFlow::Node getDispatchValue(); + + /** Gets a candidate target for this call. */ + abstract Function resolve(); + + /** + * Whether `src` can flow to this call. + * + * Searches backwards from `getDispatchValue()` to `src`. The `allowFromArg` + * parameter is true when the search is allowed to continue backwards into + * a parameter; non-recursive callers should pass `_` for `allowFromArg`. + */ + predicate flowsFrom(DataFlow::Node src, boolean allowFromArg) { + src = this.getDispatchValue() and allowFromArg = true + or + exists(DataFlow::Node other, boolean allowOtherFromArg | + this.flowsFrom(other, allowOtherFromArg) + | + // Call argument + exists(DataFlowCall call, Position i | + other + .(DataFlow::ParameterNode) + .isParameterOf(pragma[only_bind_into](call).getStaticCallTarget(), i) and + src.(ArgumentNode).argumentOf(call, pragma[only_bind_into](pragma[only_bind_out](i))) + ) and + allowOtherFromArg = true and + allowFromArg = true + or + // Call return + exists(DataFlowCall call, ReturnKind returnKind | + other = getAnOutNode(call, returnKind) and + returnNodeWithKindAndEnclosingCallable(src, returnKind, call.getStaticCallTarget()) + ) and + allowFromArg = false + or + // Local flow + DataFlow::localFlowStep(src, other) and + allowFromArg = allowOtherFromArg + or + // Flow from global variable to load. + exists(LoadInstruction load, GlobalOrNamespaceVariable var | + var = src.asVariable() and + other.asInstruction() = load and + addressOfGlobal(load.getSourceAddress(), var) and + // The `allowFromArg` concept doesn't play a role when `src` is a + // global variable, so we just set it to a single arbitrary value for + // performance. + allowFromArg = true + ) + or + // Flow from store to global variable. + exists(StoreInstruction store, GlobalOrNamespaceVariable var | + var = other.asVariable() and + store = src.asInstruction() and + storeIntoGlobal(store, var) and + // Setting `allowFromArg` to `true` like in the base case means we + // treat a store to a global variable like the dispatch itself: flow + // may come from anywhere. + allowFromArg = true + ) + ) + } + } + + pragma[noinline] + private predicate storeIntoGlobal(StoreInstruction store, GlobalOrNamespaceVariable var) { + addressOfGlobal(store.getDestinationAddress(), var) + } + + /** Holds if `addressInstr` is an instruction that produces the address of `var`. */ + private predicate addressOfGlobal(Instruction addressInstr, GlobalOrNamespaceVariable var) { + // Access directly to the global variable + addressInstr.(VariableAddressInstruction).getAstVariable() = var + or + // Access to a field on a global union + exists(FieldAddressInstruction fa | + fa = addressInstr and + fa.getObjectAddress().(VariableAddressInstruction).getAstVariable() = var and + fa.getField().getDeclaringType() instanceof Union + ) + } + + /** + * A ReturnNode with its ReturnKind and its enclosing callable. + * + * Used to fix a join ordering issue in flowsFrom. + */ + pragma[noinline] + private predicate returnNodeWithKindAndEnclosingCallable( + ReturnNode node, ReturnKind kind, DataFlowCallable callable + ) { + node.getKind() = kind and + node.getEnclosingCallable() = callable + } + + /** Call through a function pointer. */ + private class DataSensitiveExprCall extends DataSensitiveCall { + DataSensitiveExprCall() { not exists(this.getStaticCallTarget()) } + + override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getCallTarget() } + + override Function resolve() { + exists(FunctionInstruction fi | + this.flowsFrom(DataFlow::instructionNode(fi), _) and + result = fi.getFunctionSymbol() + ) and + ( + this.getNumberOfArguments() <= result.getEffectiveNumberOfParameters() and + this.getNumberOfArguments() >= result.getEffectiveNumberOfParameters() + or + result.isVarargs() + ) + } + } + + /** Call to a virtual function. */ + private class DataSensitiveOverriddenFunctionCall extends DataSensitiveCall { + DataSensitiveOverriddenFunctionCall() { + exists(this.getStaticCallTarget().(VirtualFunction).getAnOverridingFunction()) + } + + override DataFlow::Node getDispatchValue() { result.asInstruction() = this.getThisArgument() } + + override MemberFunction resolve() { + exists(Class overridingClass | + this.overrideMayAffectCall(overridingClass, result) and + this.hasFlowFromCastFrom(overridingClass) + ) + } + + /** + * Holds if `this` is a virtual function call whose static target is + * overridden by `overridingFunction` in `overridingClass`. + */ + pragma[noinline] + private predicate overrideMayAffectCall(Class overridingClass, MemberFunction overridingFunction) { + overridingFunction.getAnOverriddenFunction+() = this.getStaticCallTarget().(VirtualFunction) and + overridingFunction.getDeclaringType() = overridingClass + } + + /** + * Holds if the qualifier of `this` has flow from an upcast from + * `derivedClass`. + */ + pragma[noinline] + private predicate hasFlowFromCastFrom(Class derivedClass) { + exists(ConvertToBaseInstruction toBase | + this.flowsFrom(DataFlow::instructionNode(toBase), _) and + derivedClass = toBase.getDerivedClass() + ) + } + } +} + +/** + * Holds if `f` is a function with a body that has name `qualifiedName` and + * `nparams` parameter count. See `functionSignature`. + */ +private predicate functionSignatureWithBody(string qualifiedName, int nparams, Function f) { + functionSignature(f, qualifiedName, nparams) and + exists(f.getBlock()) +} + +/** + * Holds if the target of `call` is a function _with no definition_ that has + * name `qualifiedName` and `nparams` parameter count. See `functionSignature`. + */ +pragma[noinline] +private predicate callSignatureWithoutBody(string qualifiedName, int nparams, CallInstruction call) { + exists(Function target | + target = call.getStaticCallTarget() and + not exists(target.getBlock()) and + functionSignature(target, qualifiedName, nparams) + ) +} + +/** + * Holds if `f` has name `qualifiedName` and `nparams` parameter count. This is + * an approximation of its signature for the purpose of matching functions that + * might be the same across link targets. + */ +private predicate functionSignature(Function f, string qualifiedName, int nparams) { + qualifiedName = f.getQualifiedName() and + nparams = f.getNumberOfParameters() and + not f.isStatic() +} + +/** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. + */ +predicate mayBenefitFromCallContext(CallInstruction call, Function f) { + mayBenefitFromCallContext(call, f, _) +} + +/** + * Holds if `call` is a call through a function pointer, and the pointer + * value is given as the `arg`'th argument to `f`. + */ +private predicate mayBenefitFromCallContext( + VirtualDispatch::DataSensitiveCall call, Function f, int arg +) { + f = pragma[only_bind_out](call).getEnclosingCallable() and + exists(InitializeParameterInstruction init | + not exists(call.getStaticCallTarget()) and + init.getEnclosingFunction() = f and + call.flowsFrom(DataFlow::instructionNode(init), _) and + init.getParameter().getIndex() = arg + ) +} + +/** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ +Function viableImplInCallContext(CallInstruction call, CallInstruction ctx) { + result = viableCallable(call) and + exists(int i, Function f | + mayBenefitFromCallContext(pragma[only_bind_into](call), f, i) and + f = ctx.getStaticCallTarget() and + result = ctx.getArgument(i).getUnconvertedResultExpression().(FunctionAccess).getTarget() + ) +} + +/** Holds if arguments at position `apos` match parameters at position `ppos`. */ +pragma[inline] +predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos = apos } diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll new file mode 100644 index 00000000000..468f8640a78 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl.qll @@ -0,0 +1,4450 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. 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. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public +import DataFlowImplCommonPublic + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. 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 DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * 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 two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Node source) { none() } + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state) { none() } + + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Node sink) { none() } + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node source, FlowState state) { none() } + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + predicate isBarrier(Node node, FlowState state) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited when + * the flow state is `state` + */ + deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + */ + FlowFeature getAFeature() { none() } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (for example in a `path-problem` query). + */ + predicate includeHiddenNodes() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSource(n, _)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n | this.isSink(n, _)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.asNode().(ParamNode).isParameterOf(c, pos) + } + + ParameterPosition getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) + | + config.isSource(n) or config.isSource(n, _) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) + | + config.isSink(n) or config.isSink(n, _) + ) +} + +/** A bridge class to access the deprecated `isBarrierGuard`. */ +private class BarrierGuardGuardedNodeBridge extends Unit { + abstract predicate guardedNode(Node n, Configuration config); + + abstract predicate guardedNode(Node n, FlowState state, Configuration config); +} + +private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge { + deprecated override predicate guardedNode(Node n, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + } + + deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g, state) and + n = g.getAGuardedNode() + ) + } +} + +pragma[nomagic] +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) and + not config.isSource(n, _) + or + config.isBarrierOut(n) and + not config.isSink(n) and + not config.isSink(n, _) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config) + ) +} + +pragma[nomagic] +private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n, state) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSource(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSource(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +pragma[nomagic] +private predicate sinkNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSink(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSink(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +/** Provides the relevant barriers for a step from `node1` to `node2`. */ +pragma[inline] +private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) and + not fullBarrier(node1, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n and + not fullBarrier(node2, config) + ) +} + +private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +pragma[nomagic] +private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) { + readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and + stepFilter(node1, node2, config) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(ContentSet cs | + readSet(node1, cs, node2, config) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + +pragma[nomagic] +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), + contentType) and + read(_, tc.getContent(), _, config) and + stepFilter(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private predicate hasSourceCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate hasSinkCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private module Stage1 implements StageSig { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + sourceNode(node, _, config) and + if hasSourceCallCtx(config) then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc, config) | + localFlowStep(mid, node, config) or + additionalLocalFlowStep(mid, node, config) or + additionalLocalStateStep(mid, _, node, _, config) + ) + or + exists(NodeEx mid | fwdFlow(mid, _, config) and cc = false | + jumpStep(mid, node, config) or + additionalJumpStep(mid, node, config) or + additionalJumpStateStep(mid, _, node, _, config) + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc, config) and + fwdFlowConsCandSet(c, _, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true and + not fullBarrier(node, config) + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + readSet(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c, Configuration config) { + fwdFlowConsCand(c, config) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2, config) or + additionalJumpStateStep(node1, state1, _, state2, config) + | + fwdFlow(node1, config) + ) + } + + private predicate fwdFlowState(FlowState state, Configuration config) { + sourceNode(_, state, config) + or + exists(FlowState state0 | + fwdFlowState(state0, config) and + stateStepFwd(state0, state, config) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + exists(FlowState state | + fwdFlow(node, pragma[only_bind_into](config)) and + sinkNode(node, state, config) and + fwdFlowState(state, pragma[only_bind_into](config)) and + if hasSinkCallCtx(config) then toReturn = true else toReturn = false + ) + or + exists(NodeEx mid | revFlow(mid, toReturn, config) | + localFlowStep(node, mid, config) or + additionalLocalFlowStep(node, mid, config) or + additionalLocalStateStep(node, _, mid, _, config) + ) + or + exists(NodeEx mid | revFlow(mid, _, config) and toReturn = false | + jumpStep(node, mid, config) or + additionalJumpStep(node, mid, config) or + additionalJumpStateStep(node, _, mid, _, config) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSet(node, c, mid, config) and + fwdFlowConsCandSet(c, _, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node, pragma[only_bind_into](config)) and + readSet(node, cs, mid, config) and + fwdFlowConsCandSet(cs, c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2, config) or + additionalJumpStateStep(node1, state1, node2, state2, config) + | + revFlow(node1, _, pragma[only_bind_into](config)) and + revFlow(node2, _, pragma[only_bind_into](config)) and + fwdFlowState(state1, pragma[only_bind_into](config)) and + fwdFlowState(state2, pragma[only_bind_into](config)) + ) + } + + predicate revFlowState(FlowState state, Configuration config) { + exists(NodeEx node | + sinkNode(node, state, config) and + revFlow(node, _, pragma[only_bind_into](config)) and + fwdFlowState(state, pragma[only_bind_into](config)) + ) + or + exists(FlowState state0 | + revFlowState(state0, config) and + stateStepRev(state, state0, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private signature module StageSig { + class Ap; + + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { + class ApApprox = PrevStage::Ap; + + signature module StageParam { + class Ap; + + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); + } + + module Stage implements StageSig { + import Param + + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } + + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { + class Cc = CallContext; + + class CcCall = CallContextCall; + + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + CcCall ccSomeCall() { result instanceof CallContextSomeCall } + + module NoLocalCallContext { + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } +} + +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalStateStep(node1, state1, node2, state2, config) + ) and + exists(ap) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + predicate flowIntoCall = flowIntoCallNodeCand1/5; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + PrevStage::revFlowState(state, pragma[only_bind_into](config)) and + exists(ap) and + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage2 implements StageSig { + import MkStage::Stage +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) { + Stage2::revFlow(node, state, config) and + ( + sourceNode(node, state, config) + or + jumpStep(_, node, config) + or + additionalJumpStep(_, node, config) + or + additionalJumpStateStep(_, _, node, state, config) + or + node instanceof ParamNodeEx + or + node.asNode() instanceof OutNodeExt + or + Stage2::storeStepCand(_, _, _, node, _, config) + or + Stage2::readStepCand(_, _, node, config) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state, config) and + s != state + ) + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, state, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + Stage2::storeStepCand(node, _, _, next, _, config) or + Stage2::readStepCand(node, _, next, config) + ) + or + exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) | + additionalJumpStateStep(node, state, next, s, config) + or + additionalLocalStateStep(node, state, next, s, config) and + s != state + ) + or + Stage2::revFlow(node, state, config) and + node instanceof FlowCheckNode + or + sinkNode(node, state, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 and + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, + pragma[only_bind_into](config)) + or + additionalLocalStateStep(node1, state1, node2, state2, config) and + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + Configuration config, LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() and // irrelevant dummy value + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + or + additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + or + exists(NodeEx mid | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, + pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + AccessPathFrontNil apf, Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, state1, config) and + state1 = state2 + or + additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and + state1 != state2 and + preservesValue = false and + apf = TFrontNil(node2.getDataFlowType()) and + callContext.relevantFor(node1.getEnclosingCallable()) and + not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | + isUnreachableInCallCached(node1.asNode(), call) or + isUnreachableInCallCached(node2.asNode(), call) + ) + } +} + +private import LocalFlowBigStep + +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + import BooleanCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { + PrevStage::revFlow(node, config) and + clearsContentCached(node.asNode(), c) + } + + pragma[nomagic] + private predicate clearContent(NodeEx node, Content c, Configuration config) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _, pragma[only_bind_into](config)) and + c = cs.getAReadContent() and + clearSet(node, cs, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap, Configuration config) { + clearContent(node, ap.getHead().getContent(), config) + } + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + exists(state) and + exists(config) and + not clear(node, ap, config) and + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } +} + +private module Stage3 implements StageSig { + import MkStage::Stage +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx( + NodeEx node, FlowState state, AccessPathFront argApf, Configuration config +) { + exists(AccessPathFront apf | + Stage3::revFlow(node, state, true, _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + import Level1CallContext + import LocalCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage4 = MkStage::Stage; + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +pragma[nomagic] +private predicate nodeMayUseSummary0( + NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, _, _) and + Stage4::revFlow(n, state, true, _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +pragma[nomagic] +private predicate nodeMayUseSummary( + NodeEx n, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(DataFlowCallable c | + Stage4::parameterMayFlowThrough(_, c, apa, config) and + nodeMayUseSummary0(n, c, state, apa, config) + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { + exists(Configuration config | + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::revFlow(p, state, _, config) + ) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState s; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, s, ap) } + + ParameterPosition getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config + ) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, state, config) and + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, state, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, FlowState state, Configuration config) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.getNodeEx() and + state = sink.getState() and + config = sink.getConfiguration() + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the `FlowState` of this node. */ + FlowState getState() { none() } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getANonHiddenSuccessor() and + reach(this) and + reach(result) + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNodeImpl getASuccessorImpl(); + + private PathNodeImpl getASuccessorIfHidden() { + this.isHidden() and + result = this.getASuccessorImpl() + } + + final PathNodeImpl getANonHiddenSuccessor() { + result = this.getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + not this.getConfiguration().includeHiddenNodes() and + ( + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + ) + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNodeImpl n) { + n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNodeImpl n1, PathNode n2) { + n1.getANonHiddenSuccessor() = n2 and directReach(n2) +} + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Subpaths::subpaths(arg, par, ret, out) and + reach(arg) and + reach(par) and + reach(ret) and + reach(out) + } +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx(), result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink + result = this.getSuccMid().projectToSink() + } + + override predicate isSource() { + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + } + + predicate isAtSink() { + sinkNode(node, state, config) and + ap instanceof AccessPathNil and + if hasSinkCallCtx(config) + then + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. This also implies + // `sc instanceof SummaryCtxNone`. + // For `FeatureEqualSourceSinkCallContext` the initial call context was + // set to `CallContextSomeCall` and jumps are disallowed, so + // `cc instanceof CallContextNoCall` never holds. On the other hand, + // in this case there's never any need to enter a call except to identify + // a summary, so the condition in `pathIntoCallable` enforces this, which + // means that `sc instanceof SummaryCtxNone` holds if and only if we are + // in the call context of the source. + sc instanceof SummaryCtxNone or + cc instanceof CallContextNoCall + else any() + } + + PathNodeSink projectToSink() { + this.isAtSink() and + result.getNodeEx() = node and + result.getState() = state and + result.getConfiguration() = unbindConf(config) + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, state, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, state, config) } +} + +private predicate pathNode( + PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, + Configuration conf, LocalCallContext localCC +) { + midnode = mid.getNodeEx() and + state = mid.getState() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap = mid.getAp() +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +pragma[nomagic] +private predicate pathStep( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, ap, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, true, _, conf, localCC) + ) + or + exists( + AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC + | + pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, state, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, state, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, state, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, state, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, state, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPathApprox apa, Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, state, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, state, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(ArgNodeEx arg, ArgumentPosition apos | + pathNode(mid, arg, state, cc, _, ap, config, _) and + arg.asNode().(ArgNode).argumentOf(call, apos) and + apa = ap.getApprox() and + parameterMatch(ppos, apos) + ) +} + +pragma[nomagic] +private predicate parameterCand( + DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, apa, config) and + p.isParameterOf(callable, pos) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, ap, + pragma[only_bind_into](apa), pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, + SummaryCtx sc, DataFlowCall call, Configuration config +) { + exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + ( + sc = TSummaryCtxSome(p, state, ap) + or + not exists(TSummaryCtxSome(p, state, ap)) and + sc = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + pathNode(mid, ret, state, cc, sc, ap, config, _) and + kind = ret.getKind() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, state, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable( + PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, AccessPath ap +) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, state, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, _, innercc, sc, _, config) and + paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, + pragma[only_bind_into](apout), _, unbindConf(config)) and + not arg.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `sout`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and + pathNode(ret, retnode, sout, innercc, sc, apout, unbindConf(getPathNodeConf(arg)), _) and + kind = retnode.getKind() + ) + } + + private PathNodeImpl localStepToHidden(PathNodeImpl n) { + n.getASuccessorImpl() = result and + result.isHidden() and + exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | + localFlowBigStep(n1, _, n2, _, _, _, _, _) or + store(n1, _, n2, _, _) or + readSet(n1, _, n2, _) + ) + } + + pragma[nomagic] + private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { + succ = pred.getANonHiddenSuccessor() and + succNode = succ.getNodeEx() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { + exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | + pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and + subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and + hasSuccessor(pragma[only_bind_into](arg), par, p) and + not ret.isHidden() and + pathNode(out0, o, sout, _, _, apout, _, _) + | + out = out0 or out = out0.projectToSink() + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + predicate retReach(PathNodeImpl n) { + exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples +) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + Configuration config +) { + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + additionalJumpStateStep(node1, _, node2, _, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) or config.isSource(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) or config.isSink(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + (config.isSource(n) or config.isSource(n, _)) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + (config.isSink(n) or config.isSink(n, _)) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state, _) or + sinkNode(_, state, _) or + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = TPartialNil(node.getDataFlowType()) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, + RevPartialAccessPath ap, Configuration config + ) { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), result.getAp(), + result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, _, ap, config) + or + partialPathOutOfCallable(mid, node, state, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() + or + partialPathThroughCallable(mid, node, state, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + PartialAccessPath ap, Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, pos, state, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3 | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, call, _, config) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, state, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, RevPartialAccessPath ap, + Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, RevPartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + RevPartialAccessPath ap, Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _, config) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, RevPartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll new file mode 100644 index 00000000000..468f8640a78 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl2.qll @@ -0,0 +1,4450 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. 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. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public +import DataFlowImplCommonPublic + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. 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 DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * 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 two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Node source) { none() } + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state) { none() } + + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Node sink) { none() } + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node source, FlowState state) { none() } + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + predicate isBarrier(Node node, FlowState state) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited when + * the flow state is `state` + */ + deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + */ + FlowFeature getAFeature() { none() } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (for example in a `path-problem` query). + */ + predicate includeHiddenNodes() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSource(n, _)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n | this.isSink(n, _)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.asNode().(ParamNode).isParameterOf(c, pos) + } + + ParameterPosition getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) + | + config.isSource(n) or config.isSource(n, _) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) + | + config.isSink(n) or config.isSink(n, _) + ) +} + +/** A bridge class to access the deprecated `isBarrierGuard`. */ +private class BarrierGuardGuardedNodeBridge extends Unit { + abstract predicate guardedNode(Node n, Configuration config); + + abstract predicate guardedNode(Node n, FlowState state, Configuration config); +} + +private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge { + deprecated override predicate guardedNode(Node n, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + } + + deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g, state) and + n = g.getAGuardedNode() + ) + } +} + +pragma[nomagic] +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) and + not config.isSource(n, _) + or + config.isBarrierOut(n) and + not config.isSink(n) and + not config.isSink(n, _) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config) + ) +} + +pragma[nomagic] +private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n, state) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSource(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSource(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +pragma[nomagic] +private predicate sinkNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSink(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSink(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +/** Provides the relevant barriers for a step from `node1` to `node2`. */ +pragma[inline] +private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) and + not fullBarrier(node1, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n and + not fullBarrier(node2, config) + ) +} + +private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +pragma[nomagic] +private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) { + readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and + stepFilter(node1, node2, config) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(ContentSet cs | + readSet(node1, cs, node2, config) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + +pragma[nomagic] +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), + contentType) and + read(_, tc.getContent(), _, config) and + stepFilter(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private predicate hasSourceCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate hasSinkCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private module Stage1 implements StageSig { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + sourceNode(node, _, config) and + if hasSourceCallCtx(config) then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc, config) | + localFlowStep(mid, node, config) or + additionalLocalFlowStep(mid, node, config) or + additionalLocalStateStep(mid, _, node, _, config) + ) + or + exists(NodeEx mid | fwdFlow(mid, _, config) and cc = false | + jumpStep(mid, node, config) or + additionalJumpStep(mid, node, config) or + additionalJumpStateStep(mid, _, node, _, config) + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc, config) and + fwdFlowConsCandSet(c, _, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true and + not fullBarrier(node, config) + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + readSet(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c, Configuration config) { + fwdFlowConsCand(c, config) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2, config) or + additionalJumpStateStep(node1, state1, _, state2, config) + | + fwdFlow(node1, config) + ) + } + + private predicate fwdFlowState(FlowState state, Configuration config) { + sourceNode(_, state, config) + or + exists(FlowState state0 | + fwdFlowState(state0, config) and + stateStepFwd(state0, state, config) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + exists(FlowState state | + fwdFlow(node, pragma[only_bind_into](config)) and + sinkNode(node, state, config) and + fwdFlowState(state, pragma[only_bind_into](config)) and + if hasSinkCallCtx(config) then toReturn = true else toReturn = false + ) + or + exists(NodeEx mid | revFlow(mid, toReturn, config) | + localFlowStep(node, mid, config) or + additionalLocalFlowStep(node, mid, config) or + additionalLocalStateStep(node, _, mid, _, config) + ) + or + exists(NodeEx mid | revFlow(mid, _, config) and toReturn = false | + jumpStep(node, mid, config) or + additionalJumpStep(node, mid, config) or + additionalJumpStateStep(node, _, mid, _, config) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSet(node, c, mid, config) and + fwdFlowConsCandSet(c, _, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node, pragma[only_bind_into](config)) and + readSet(node, cs, mid, config) and + fwdFlowConsCandSet(cs, c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2, config) or + additionalJumpStateStep(node1, state1, node2, state2, config) + | + revFlow(node1, _, pragma[only_bind_into](config)) and + revFlow(node2, _, pragma[only_bind_into](config)) and + fwdFlowState(state1, pragma[only_bind_into](config)) and + fwdFlowState(state2, pragma[only_bind_into](config)) + ) + } + + predicate revFlowState(FlowState state, Configuration config) { + exists(NodeEx node | + sinkNode(node, state, config) and + revFlow(node, _, pragma[only_bind_into](config)) and + fwdFlowState(state, pragma[only_bind_into](config)) + ) + or + exists(FlowState state0 | + revFlowState(state0, config) and + stateStepRev(state, state0, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private signature module StageSig { + class Ap; + + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { + class ApApprox = PrevStage::Ap; + + signature module StageParam { + class Ap; + + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); + } + + module Stage implements StageSig { + import Param + + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } + + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { + class Cc = CallContext; + + class CcCall = CallContextCall; + + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + CcCall ccSomeCall() { result instanceof CallContextSomeCall } + + module NoLocalCallContext { + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } +} + +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalStateStep(node1, state1, node2, state2, config) + ) and + exists(ap) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + predicate flowIntoCall = flowIntoCallNodeCand1/5; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + PrevStage::revFlowState(state, pragma[only_bind_into](config)) and + exists(ap) and + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage2 implements StageSig { + import MkStage::Stage +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) { + Stage2::revFlow(node, state, config) and + ( + sourceNode(node, state, config) + or + jumpStep(_, node, config) + or + additionalJumpStep(_, node, config) + or + additionalJumpStateStep(_, _, node, state, config) + or + node instanceof ParamNodeEx + or + node.asNode() instanceof OutNodeExt + or + Stage2::storeStepCand(_, _, _, node, _, config) + or + Stage2::readStepCand(_, _, node, config) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state, config) and + s != state + ) + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, state, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + Stage2::storeStepCand(node, _, _, next, _, config) or + Stage2::readStepCand(node, _, next, config) + ) + or + exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) | + additionalJumpStateStep(node, state, next, s, config) + or + additionalLocalStateStep(node, state, next, s, config) and + s != state + ) + or + Stage2::revFlow(node, state, config) and + node instanceof FlowCheckNode + or + sinkNode(node, state, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 and + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, + pragma[only_bind_into](config)) + or + additionalLocalStateStep(node1, state1, node2, state2, config) and + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + Configuration config, LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() and // irrelevant dummy value + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + or + additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + or + exists(NodeEx mid | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, + pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + AccessPathFrontNil apf, Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, state1, config) and + state1 = state2 + or + additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and + state1 != state2 and + preservesValue = false and + apf = TFrontNil(node2.getDataFlowType()) and + callContext.relevantFor(node1.getEnclosingCallable()) and + not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | + isUnreachableInCallCached(node1.asNode(), call) or + isUnreachableInCallCached(node2.asNode(), call) + ) + } +} + +private import LocalFlowBigStep + +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + import BooleanCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { + PrevStage::revFlow(node, config) and + clearsContentCached(node.asNode(), c) + } + + pragma[nomagic] + private predicate clearContent(NodeEx node, Content c, Configuration config) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _, pragma[only_bind_into](config)) and + c = cs.getAReadContent() and + clearSet(node, cs, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap, Configuration config) { + clearContent(node, ap.getHead().getContent(), config) + } + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + exists(state) and + exists(config) and + not clear(node, ap, config) and + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } +} + +private module Stage3 implements StageSig { + import MkStage::Stage +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx( + NodeEx node, FlowState state, AccessPathFront argApf, Configuration config +) { + exists(AccessPathFront apf | + Stage3::revFlow(node, state, true, _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + import Level1CallContext + import LocalCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage4 = MkStage::Stage; + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +pragma[nomagic] +private predicate nodeMayUseSummary0( + NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, _, _) and + Stage4::revFlow(n, state, true, _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +pragma[nomagic] +private predicate nodeMayUseSummary( + NodeEx n, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(DataFlowCallable c | + Stage4::parameterMayFlowThrough(_, c, apa, config) and + nodeMayUseSummary0(n, c, state, apa, config) + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { + exists(Configuration config | + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::revFlow(p, state, _, config) + ) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState s; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, s, ap) } + + ParameterPosition getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config + ) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, state, config) and + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, state, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, FlowState state, Configuration config) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.getNodeEx() and + state = sink.getState() and + config = sink.getConfiguration() + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the `FlowState` of this node. */ + FlowState getState() { none() } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getANonHiddenSuccessor() and + reach(this) and + reach(result) + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNodeImpl getASuccessorImpl(); + + private PathNodeImpl getASuccessorIfHidden() { + this.isHidden() and + result = this.getASuccessorImpl() + } + + final PathNodeImpl getANonHiddenSuccessor() { + result = this.getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + not this.getConfiguration().includeHiddenNodes() and + ( + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + ) + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNodeImpl n) { + n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNodeImpl n1, PathNode n2) { + n1.getANonHiddenSuccessor() = n2 and directReach(n2) +} + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Subpaths::subpaths(arg, par, ret, out) and + reach(arg) and + reach(par) and + reach(ret) and + reach(out) + } +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx(), result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink + result = this.getSuccMid().projectToSink() + } + + override predicate isSource() { + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + } + + predicate isAtSink() { + sinkNode(node, state, config) and + ap instanceof AccessPathNil and + if hasSinkCallCtx(config) + then + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. This also implies + // `sc instanceof SummaryCtxNone`. + // For `FeatureEqualSourceSinkCallContext` the initial call context was + // set to `CallContextSomeCall` and jumps are disallowed, so + // `cc instanceof CallContextNoCall` never holds. On the other hand, + // in this case there's never any need to enter a call except to identify + // a summary, so the condition in `pathIntoCallable` enforces this, which + // means that `sc instanceof SummaryCtxNone` holds if and only if we are + // in the call context of the source. + sc instanceof SummaryCtxNone or + cc instanceof CallContextNoCall + else any() + } + + PathNodeSink projectToSink() { + this.isAtSink() and + result.getNodeEx() = node and + result.getState() = state and + result.getConfiguration() = unbindConf(config) + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, state, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, state, config) } +} + +private predicate pathNode( + PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, + Configuration conf, LocalCallContext localCC +) { + midnode = mid.getNodeEx() and + state = mid.getState() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap = mid.getAp() +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +pragma[nomagic] +private predicate pathStep( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, ap, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, true, _, conf, localCC) + ) + or + exists( + AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC + | + pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, state, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, state, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, state, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, state, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, state, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPathApprox apa, Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, state, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, state, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(ArgNodeEx arg, ArgumentPosition apos | + pathNode(mid, arg, state, cc, _, ap, config, _) and + arg.asNode().(ArgNode).argumentOf(call, apos) and + apa = ap.getApprox() and + parameterMatch(ppos, apos) + ) +} + +pragma[nomagic] +private predicate parameterCand( + DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, apa, config) and + p.isParameterOf(callable, pos) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, ap, + pragma[only_bind_into](apa), pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, + SummaryCtx sc, DataFlowCall call, Configuration config +) { + exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + ( + sc = TSummaryCtxSome(p, state, ap) + or + not exists(TSummaryCtxSome(p, state, ap)) and + sc = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + pathNode(mid, ret, state, cc, sc, ap, config, _) and + kind = ret.getKind() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, state, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable( + PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, AccessPath ap +) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, state, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, _, innercc, sc, _, config) and + paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, + pragma[only_bind_into](apout), _, unbindConf(config)) and + not arg.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `sout`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and + pathNode(ret, retnode, sout, innercc, sc, apout, unbindConf(getPathNodeConf(arg)), _) and + kind = retnode.getKind() + ) + } + + private PathNodeImpl localStepToHidden(PathNodeImpl n) { + n.getASuccessorImpl() = result and + result.isHidden() and + exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | + localFlowBigStep(n1, _, n2, _, _, _, _, _) or + store(n1, _, n2, _, _) or + readSet(n1, _, n2, _) + ) + } + + pragma[nomagic] + private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { + succ = pred.getANonHiddenSuccessor() and + succNode = succ.getNodeEx() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { + exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | + pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and + subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and + hasSuccessor(pragma[only_bind_into](arg), par, p) and + not ret.isHidden() and + pathNode(out0, o, sout, _, _, apout, _, _) + | + out = out0 or out = out0.projectToSink() + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + predicate retReach(PathNodeImpl n) { + exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples +) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + Configuration config +) { + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + additionalJumpStateStep(node1, _, node2, _, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) or config.isSource(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) or config.isSink(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + (config.isSource(n) or config.isSource(n, _)) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + (config.isSink(n) or config.isSink(n, _)) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state, _) or + sinkNode(_, state, _) or + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = TPartialNil(node.getDataFlowType()) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, + RevPartialAccessPath ap, Configuration config + ) { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), result.getAp(), + result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, _, ap, config) + or + partialPathOutOfCallable(mid, node, state, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() + or + partialPathThroughCallable(mid, node, state, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + PartialAccessPath ap, Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, pos, state, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3 | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, call, _, config) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, state, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, RevPartialAccessPath ap, + Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, RevPartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + RevPartialAccessPath ap, Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _, config) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, RevPartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll new file mode 100644 index 00000000000..468f8640a78 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl3.qll @@ -0,0 +1,4450 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. 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. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public +import DataFlowImplCommonPublic + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. 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 DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * 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 two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Node source) { none() } + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state) { none() } + + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Node sink) { none() } + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node source, FlowState state) { none() } + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + predicate isBarrier(Node node, FlowState state) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited when + * the flow state is `state` + */ + deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + */ + FlowFeature getAFeature() { none() } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (for example in a `path-problem` query). + */ + predicate includeHiddenNodes() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSource(n, _)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n | this.isSink(n, _)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.asNode().(ParamNode).isParameterOf(c, pos) + } + + ParameterPosition getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) + | + config.isSource(n) or config.isSource(n, _) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) + | + config.isSink(n) or config.isSink(n, _) + ) +} + +/** A bridge class to access the deprecated `isBarrierGuard`. */ +private class BarrierGuardGuardedNodeBridge extends Unit { + abstract predicate guardedNode(Node n, Configuration config); + + abstract predicate guardedNode(Node n, FlowState state, Configuration config); +} + +private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge { + deprecated override predicate guardedNode(Node n, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + } + + deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g, state) and + n = g.getAGuardedNode() + ) + } +} + +pragma[nomagic] +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) and + not config.isSource(n, _) + or + config.isBarrierOut(n) and + not config.isSink(n) and + not config.isSink(n, _) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config) + ) +} + +pragma[nomagic] +private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n, state) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSource(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSource(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +pragma[nomagic] +private predicate sinkNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSink(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSink(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +/** Provides the relevant barriers for a step from `node1` to `node2`. */ +pragma[inline] +private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) and + not fullBarrier(node1, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n and + not fullBarrier(node2, config) + ) +} + +private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +pragma[nomagic] +private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) { + readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and + stepFilter(node1, node2, config) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(ContentSet cs | + readSet(node1, cs, node2, config) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + +pragma[nomagic] +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), + contentType) and + read(_, tc.getContent(), _, config) and + stepFilter(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private predicate hasSourceCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate hasSinkCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private module Stage1 implements StageSig { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + sourceNode(node, _, config) and + if hasSourceCallCtx(config) then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc, config) | + localFlowStep(mid, node, config) or + additionalLocalFlowStep(mid, node, config) or + additionalLocalStateStep(mid, _, node, _, config) + ) + or + exists(NodeEx mid | fwdFlow(mid, _, config) and cc = false | + jumpStep(mid, node, config) or + additionalJumpStep(mid, node, config) or + additionalJumpStateStep(mid, _, node, _, config) + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc, config) and + fwdFlowConsCandSet(c, _, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true and + not fullBarrier(node, config) + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + readSet(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c, Configuration config) { + fwdFlowConsCand(c, config) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2, config) or + additionalJumpStateStep(node1, state1, _, state2, config) + | + fwdFlow(node1, config) + ) + } + + private predicate fwdFlowState(FlowState state, Configuration config) { + sourceNode(_, state, config) + or + exists(FlowState state0 | + fwdFlowState(state0, config) and + stateStepFwd(state0, state, config) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + exists(FlowState state | + fwdFlow(node, pragma[only_bind_into](config)) and + sinkNode(node, state, config) and + fwdFlowState(state, pragma[only_bind_into](config)) and + if hasSinkCallCtx(config) then toReturn = true else toReturn = false + ) + or + exists(NodeEx mid | revFlow(mid, toReturn, config) | + localFlowStep(node, mid, config) or + additionalLocalFlowStep(node, mid, config) or + additionalLocalStateStep(node, _, mid, _, config) + ) + or + exists(NodeEx mid | revFlow(mid, _, config) and toReturn = false | + jumpStep(node, mid, config) or + additionalJumpStep(node, mid, config) or + additionalJumpStateStep(node, _, mid, _, config) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSet(node, c, mid, config) and + fwdFlowConsCandSet(c, _, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node, pragma[only_bind_into](config)) and + readSet(node, cs, mid, config) and + fwdFlowConsCandSet(cs, c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2, config) or + additionalJumpStateStep(node1, state1, node2, state2, config) + | + revFlow(node1, _, pragma[only_bind_into](config)) and + revFlow(node2, _, pragma[only_bind_into](config)) and + fwdFlowState(state1, pragma[only_bind_into](config)) and + fwdFlowState(state2, pragma[only_bind_into](config)) + ) + } + + predicate revFlowState(FlowState state, Configuration config) { + exists(NodeEx node | + sinkNode(node, state, config) and + revFlow(node, _, pragma[only_bind_into](config)) and + fwdFlowState(state, pragma[only_bind_into](config)) + ) + or + exists(FlowState state0 | + revFlowState(state0, config) and + stateStepRev(state, state0, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private signature module StageSig { + class Ap; + + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { + class ApApprox = PrevStage::Ap; + + signature module StageParam { + class Ap; + + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); + } + + module Stage implements StageSig { + import Param + + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } + + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { + class Cc = CallContext; + + class CcCall = CallContextCall; + + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + CcCall ccSomeCall() { result instanceof CallContextSomeCall } + + module NoLocalCallContext { + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } +} + +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalStateStep(node1, state1, node2, state2, config) + ) and + exists(ap) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + predicate flowIntoCall = flowIntoCallNodeCand1/5; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + PrevStage::revFlowState(state, pragma[only_bind_into](config)) and + exists(ap) and + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage2 implements StageSig { + import MkStage::Stage +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) { + Stage2::revFlow(node, state, config) and + ( + sourceNode(node, state, config) + or + jumpStep(_, node, config) + or + additionalJumpStep(_, node, config) + or + additionalJumpStateStep(_, _, node, state, config) + or + node instanceof ParamNodeEx + or + node.asNode() instanceof OutNodeExt + or + Stage2::storeStepCand(_, _, _, node, _, config) + or + Stage2::readStepCand(_, _, node, config) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state, config) and + s != state + ) + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, state, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + Stage2::storeStepCand(node, _, _, next, _, config) or + Stage2::readStepCand(node, _, next, config) + ) + or + exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) | + additionalJumpStateStep(node, state, next, s, config) + or + additionalLocalStateStep(node, state, next, s, config) and + s != state + ) + or + Stage2::revFlow(node, state, config) and + node instanceof FlowCheckNode + or + sinkNode(node, state, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 and + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, + pragma[only_bind_into](config)) + or + additionalLocalStateStep(node1, state1, node2, state2, config) and + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + Configuration config, LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() and // irrelevant dummy value + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + or + additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + or + exists(NodeEx mid | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, + pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + AccessPathFrontNil apf, Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, state1, config) and + state1 = state2 + or + additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and + state1 != state2 and + preservesValue = false and + apf = TFrontNil(node2.getDataFlowType()) and + callContext.relevantFor(node1.getEnclosingCallable()) and + not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | + isUnreachableInCallCached(node1.asNode(), call) or + isUnreachableInCallCached(node2.asNode(), call) + ) + } +} + +private import LocalFlowBigStep + +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + import BooleanCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { + PrevStage::revFlow(node, config) and + clearsContentCached(node.asNode(), c) + } + + pragma[nomagic] + private predicate clearContent(NodeEx node, Content c, Configuration config) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _, pragma[only_bind_into](config)) and + c = cs.getAReadContent() and + clearSet(node, cs, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap, Configuration config) { + clearContent(node, ap.getHead().getContent(), config) + } + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + exists(state) and + exists(config) and + not clear(node, ap, config) and + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } +} + +private module Stage3 implements StageSig { + import MkStage::Stage +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx( + NodeEx node, FlowState state, AccessPathFront argApf, Configuration config +) { + exists(AccessPathFront apf | + Stage3::revFlow(node, state, true, _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + import Level1CallContext + import LocalCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage4 = MkStage::Stage; + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +pragma[nomagic] +private predicate nodeMayUseSummary0( + NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, _, _) and + Stage4::revFlow(n, state, true, _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +pragma[nomagic] +private predicate nodeMayUseSummary( + NodeEx n, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(DataFlowCallable c | + Stage4::parameterMayFlowThrough(_, c, apa, config) and + nodeMayUseSummary0(n, c, state, apa, config) + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { + exists(Configuration config | + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::revFlow(p, state, _, config) + ) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState s; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, s, ap) } + + ParameterPosition getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config + ) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, state, config) and + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, state, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, FlowState state, Configuration config) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.getNodeEx() and + state = sink.getState() and + config = sink.getConfiguration() + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the `FlowState` of this node. */ + FlowState getState() { none() } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getANonHiddenSuccessor() and + reach(this) and + reach(result) + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNodeImpl getASuccessorImpl(); + + private PathNodeImpl getASuccessorIfHidden() { + this.isHidden() and + result = this.getASuccessorImpl() + } + + final PathNodeImpl getANonHiddenSuccessor() { + result = this.getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + not this.getConfiguration().includeHiddenNodes() and + ( + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + ) + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNodeImpl n) { + n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNodeImpl n1, PathNode n2) { + n1.getANonHiddenSuccessor() = n2 and directReach(n2) +} + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Subpaths::subpaths(arg, par, ret, out) and + reach(arg) and + reach(par) and + reach(ret) and + reach(out) + } +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx(), result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink + result = this.getSuccMid().projectToSink() + } + + override predicate isSource() { + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + } + + predicate isAtSink() { + sinkNode(node, state, config) and + ap instanceof AccessPathNil and + if hasSinkCallCtx(config) + then + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. This also implies + // `sc instanceof SummaryCtxNone`. + // For `FeatureEqualSourceSinkCallContext` the initial call context was + // set to `CallContextSomeCall` and jumps are disallowed, so + // `cc instanceof CallContextNoCall` never holds. On the other hand, + // in this case there's never any need to enter a call except to identify + // a summary, so the condition in `pathIntoCallable` enforces this, which + // means that `sc instanceof SummaryCtxNone` holds if and only if we are + // in the call context of the source. + sc instanceof SummaryCtxNone or + cc instanceof CallContextNoCall + else any() + } + + PathNodeSink projectToSink() { + this.isAtSink() and + result.getNodeEx() = node and + result.getState() = state and + result.getConfiguration() = unbindConf(config) + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, state, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, state, config) } +} + +private predicate pathNode( + PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, + Configuration conf, LocalCallContext localCC +) { + midnode = mid.getNodeEx() and + state = mid.getState() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap = mid.getAp() +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +pragma[nomagic] +private predicate pathStep( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, ap, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, true, _, conf, localCC) + ) + or + exists( + AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC + | + pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, state, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, state, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, state, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, state, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, state, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPathApprox apa, Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, state, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, state, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(ArgNodeEx arg, ArgumentPosition apos | + pathNode(mid, arg, state, cc, _, ap, config, _) and + arg.asNode().(ArgNode).argumentOf(call, apos) and + apa = ap.getApprox() and + parameterMatch(ppos, apos) + ) +} + +pragma[nomagic] +private predicate parameterCand( + DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, apa, config) and + p.isParameterOf(callable, pos) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, ap, + pragma[only_bind_into](apa), pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, + SummaryCtx sc, DataFlowCall call, Configuration config +) { + exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + ( + sc = TSummaryCtxSome(p, state, ap) + or + not exists(TSummaryCtxSome(p, state, ap)) and + sc = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + pathNode(mid, ret, state, cc, sc, ap, config, _) and + kind = ret.getKind() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, state, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable( + PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, AccessPath ap +) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, state, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, _, innercc, sc, _, config) and + paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, + pragma[only_bind_into](apout), _, unbindConf(config)) and + not arg.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `sout`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and + pathNode(ret, retnode, sout, innercc, sc, apout, unbindConf(getPathNodeConf(arg)), _) and + kind = retnode.getKind() + ) + } + + private PathNodeImpl localStepToHidden(PathNodeImpl n) { + n.getASuccessorImpl() = result and + result.isHidden() and + exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | + localFlowBigStep(n1, _, n2, _, _, _, _, _) or + store(n1, _, n2, _, _) or + readSet(n1, _, n2, _) + ) + } + + pragma[nomagic] + private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { + succ = pred.getANonHiddenSuccessor() and + succNode = succ.getNodeEx() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { + exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | + pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and + subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and + hasSuccessor(pragma[only_bind_into](arg), par, p) and + not ret.isHidden() and + pathNode(out0, o, sout, _, _, apout, _, _) + | + out = out0 or out = out0.projectToSink() + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + predicate retReach(PathNodeImpl n) { + exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples +) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + Configuration config +) { + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + additionalJumpStateStep(node1, _, node2, _, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) or config.isSource(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) or config.isSink(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + (config.isSource(n) or config.isSource(n, _)) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + (config.isSink(n) or config.isSink(n, _)) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state, _) or + sinkNode(_, state, _) or + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = TPartialNil(node.getDataFlowType()) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, + RevPartialAccessPath ap, Configuration config + ) { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), result.getAp(), + result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, _, ap, config) + or + partialPathOutOfCallable(mid, node, state, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() + or + partialPathThroughCallable(mid, node, state, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + PartialAccessPath ap, Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, pos, state, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3 | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, call, _, config) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, state, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, RevPartialAccessPath ap, + Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, RevPartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + RevPartialAccessPath ap, Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _, config) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, RevPartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll new file mode 100644 index 00000000000..468f8640a78 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImpl4.qll @@ -0,0 +1,4450 @@ +/** + * Provides an implementation of global (interprocedural) data flow. This file + * re-exports the local (intraprocedural) data flow analysis from + * `DataFlowImplSpecific::Public` and adds a global analysis, mainly exposed + * through the `Configuration` class. 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. + */ + +private import DataFlowImplCommon +private import DataFlowImplSpecific::Private +import DataFlowImplSpecific::Public +import DataFlowImplCommonPublic + +/** + * A configuration of interprocedural data flow analysis. This defines + * sources, sinks, and any other configurable aspect of the analysis. Each + * use of the global data flow library must define its own unique extension + * of this abstract class. 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 DataFlow::Configuration { + * MyAnalysisConfiguration() { this = "MyAnalysisConfiguration" } + * // Override `isSource` and `isSink`. + * // Optionally override `isBarrier`. + * // Optionally override `isAdditionalFlowStep`. + * } + * ``` + * Conceptually, this defines a graph where the nodes are `DataFlow::Node`s and + * the edges are those data-flow steps that preserve the value of the node + * along with any additional edges defined by `isAdditionalFlowStep`. + * Specifying nodes in `isBarrier` will remove those nodes from the graph, and + * specifying nodes in `isBarrierIn` and/or `isBarrierOut` will remove in-going + * and/or out-going edges from those nodes, respectively. + * + * 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 two classes extending + * `DataFlow::Configuration` should never depend on each other. One of them + * should instead depend on a `DataFlow2::Configuration`, a + * `DataFlow3::Configuration`, or a `DataFlow4::Configuration`. + */ +abstract class Configuration extends string { + bindingset[this] + Configuration() { any() } + + /** + * Holds if `source` is a relevant data flow source. + */ + predicate isSource(Node source) { none() } + + /** + * Holds if `source` is a relevant data flow source with the given initial + * `state`. + */ + predicate isSource(Node source, FlowState state) { none() } + + /** + * Holds if `sink` is a relevant data flow sink. + */ + predicate isSink(Node sink) { none() } + + /** + * Holds if `sink` is a relevant data flow sink accepting `state`. + */ + predicate isSink(Node source, FlowState state) { none() } + + /** + * Holds if data flow through `node` is prohibited. This completely removes + * `node` from the data flow graph. + */ + predicate isBarrier(Node node) { none() } + + /** + * Holds if data flow through `node` is prohibited when the flow state is + * `state`. + */ + predicate isBarrier(Node node, FlowState state) { none() } + + /** Holds if data flow into `node` is prohibited. */ + predicate isBarrierIn(Node node) { none() } + + /** Holds if data flow out of `node` is prohibited. */ + predicate isBarrierOut(Node node) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isBarrierGuard(BarrierGuard guard) { none() } + + /** + * DEPRECATED: Use `isBarrier` and `BarrierGuard` module instead. + * + * Holds if data flow through nodes guarded by `guard` is prohibited when + * the flow state is `state` + */ + deprecated predicate isBarrierGuard(BarrierGuard guard, FlowState state) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + */ + predicate isAdditionalFlowStep(Node node1, Node node2) { none() } + + /** + * Holds if data may flow from `node1` to `node2` in addition to the normal data-flow steps. + * This step is only applicable in `state1` and updates the flow state to `state2`. + */ + predicate isAdditionalFlowStep(Node node1, FlowState state1, Node node2, FlowState state2) { + none() + } + + /** + * Holds if an arbitrary number of implicit read steps of content `c` may be + * taken at `node`. + */ + predicate allowImplicitRead(Node node, ContentSet c) { none() } + + /** + * Gets the virtual dispatch branching limit when calculating field flow. + * This can be overridden to a smaller value to improve performance (a + * value of 0 disables field flow), or a larger value to get more results. + */ + int fieldFlowBranchLimit() { result = 2 } + + /** + * Gets a data flow configuration feature to add restrictions to the set of + * valid flow paths. + * + * - `FeatureHasSourceCallContext`: + * Assume that sources have some existing call context to disallow + * conflicting return-flow directly following the source. + * - `FeatureHasSinkCallContext`: + * Assume that sinks have some existing call context to disallow + * conflicting argument-to-parameter flow directly preceding the sink. + * - `FeatureEqualSourceSinkCallContext`: + * Implies both of the above and additionally ensures that the entire flow + * path preserves the call context. + */ + FlowFeature getAFeature() { none() } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + */ + predicate hasFlow(Node source, Node sink) { flowsTo(source, sink, this) } + + /** + * Holds if data may flow from `source` to `sink` for this configuration. + * + * The corresponding paths are generated from the end-points and the graph + * included in the module `PathGraph`. + */ + predicate hasFlowPath(PathNode source, PathNode sink) { flowsTo(source, sink, _, _, this) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowTo(Node sink) { this.hasFlow(_, sink) } + + /** + * Holds if data may flow from some source to `sink` for this configuration. + */ + predicate hasFlowToExpr(DataFlowExpr sink) { this.hasFlowTo(exprNode(sink)) } + + /** + * Gets the exploration limit for `hasPartialFlow` and `hasPartialFlowRev` + * measured in approximate number of interprocedural steps. + */ + int explorationLimit() { none() } + + /** + * Holds if hidden nodes should be included in the data flow graph. + * + * This feature should only be used for debugging or when the data flow graph + * is not visualized (for example in a `path-problem` query). + */ + predicate includeHiddenNodes() { none() } + + /** + * Holds if there is a partial data flow path from `source` to `node`. The + * approximate distance between `node` and the closest source is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards sink definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sources is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + */ + final predicate hasPartialFlow(PartialPathNode source, PartialPathNode node, int dist) { + partialFlow(source, node, this) and + dist = node.getSourceDistance() + } + + /** + * Holds if there is a partial data flow path from `node` to `sink`. The + * approximate distance between `node` and the closest sink is `dist` and + * is restricted to be less than or equal to `explorationLimit()`. This + * predicate completely disregards source definitions. + * + * This predicate is intended for data-flow exploration and debugging and may + * perform poorly if the number of sinks is too big and/or the exploration + * limit is set too high without using barriers. + * + * This predicate is disabled (has no results) by default. Override + * `explorationLimit()` with a suitable number to enable this predicate. + * + * To use this in a `path-problem` query, import the module `PartialPathGraph`. + * + * Note that reverse flow has slightly lower precision than the corresponding + * forward flow, as reverse flow disregards type pruning among other features. + */ + final predicate hasPartialFlowRev(PartialPathNode node, PartialPathNode sink, int dist) { + revPartialFlow(node, sink, this) and + dist = node.getSinkDistance() + } +} + +/** + * This class exists to prevent mutual recursion between the user-overridden + * member predicates of `Configuration` and the rest of the data-flow library. + * Good performance cannot be guaranteed in the presence of such recursion, so + * it should be replaced by using more than one copy of the data flow library. + */ +abstract private class ConfigurationRecursionPrevention extends Configuration { + bindingset[this] + ConfigurationRecursionPrevention() { any() } + + override predicate hasFlow(Node source, Node sink) { + strictcount(Node n | this.isSource(n)) < 0 + or + strictcount(Node n | this.isSource(n, _)) < 0 + or + strictcount(Node n | this.isSink(n)) < 0 + or + strictcount(Node n | this.isSink(n, _)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, n2)) < 0 + or + strictcount(Node n1, Node n2 | this.isAdditionalFlowStep(n1, _, n2, _)) < 0 + or + super.hasFlow(source, sink) + } +} + +private newtype TNodeEx = + TNodeNormal(Node n) or + TNodeImplicitRead(Node n, boolean hasRead) { + any(Configuration c).allowImplicitRead(n, _) and hasRead = [false, true] + } + +private class NodeEx extends TNodeEx { + string toString() { + result = this.asNode().toString() + or + exists(Node n | this.isImplicitReadNode(n, _) | result = n.toString() + " [Ext]") + } + + Node asNode() { this = TNodeNormal(result) } + + predicate isImplicitReadNode(Node n, boolean hasRead) { this = TNodeImplicitRead(n, hasRead) } + + Node projectToNode() { this = TNodeNormal(result) or this = TNodeImplicitRead(result, _) } + + pragma[nomagic] + private DataFlowCallable getEnclosingCallable0() { + nodeEnclosingCallable(this.projectToNode(), result) + } + + pragma[inline] + DataFlowCallable getEnclosingCallable() { + pragma[only_bind_out](this).getEnclosingCallable0() = pragma[only_bind_into](result) + } + + pragma[nomagic] + private DataFlowType getDataFlowType0() { nodeDataFlowType(this.asNode(), result) } + + pragma[inline] + DataFlowType getDataFlowType() { + pragma[only_bind_out](this).getDataFlowType0() = pragma[only_bind_into](result) + } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.projectToNode().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +private class ArgNodeEx extends NodeEx { + ArgNodeEx() { this.asNode() instanceof ArgNode } +} + +private class ParamNodeEx extends NodeEx { + ParamNodeEx() { this.asNode() instanceof ParamNode } + + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + this.asNode().(ParamNode).isParameterOf(c, pos) + } + + ParameterPosition getPosition() { this.isParameterOf(_, result) } + + predicate allowParameterReturnInSelf() { allowParameterReturnInSelfCached(this.asNode()) } +} + +private class RetNodeEx extends NodeEx { + RetNodeEx() { this.asNode() instanceof ReturnNodeExt } + + ReturnPosition getReturnPosition() { result = getReturnPosition(this.asNode()) } + + ReturnKindExt getKind() { result = this.asNode().(ReturnNodeExt).getKind() } +} + +private predicate inBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierIn(n) + | + config.isSource(n) or config.isSource(n, _) + ) +} + +private predicate outBarrier(NodeEx node, Configuration config) { + exists(Node n | + node.asNode() = n and + config.isBarrierOut(n) + | + config.isSink(n) or config.isSink(n, _) + ) +} + +/** A bridge class to access the deprecated `isBarrierGuard`. */ +private class BarrierGuardGuardedNodeBridge extends Unit { + abstract predicate guardedNode(Node n, Configuration config); + + abstract predicate guardedNode(Node n, FlowState state, Configuration config); +} + +private class BarrierGuardGuardedNode extends BarrierGuardGuardedNodeBridge { + deprecated override predicate guardedNode(Node n, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g) and + n = g.getAGuardedNode() + ) + } + + deprecated override predicate guardedNode(Node n, FlowState state, Configuration config) { + exists(BarrierGuard g | + config.isBarrierGuard(g, state) and + n = g.getAGuardedNode() + ) + } +} + +pragma[nomagic] +private predicate fullBarrier(NodeEx node, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n) + or + config.isBarrierIn(n) and + not config.isSource(n) and + not config.isSource(n, _) + or + config.isBarrierOut(n) and + not config.isSink(n) and + not config.isSink(n, _) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, config) + ) +} + +pragma[nomagic] +private predicate stateBarrier(NodeEx node, FlowState state, Configuration config) { + exists(Node n | node.asNode() = n | + config.isBarrier(n, state) + or + any(BarrierGuardGuardedNodeBridge b).guardedNode(n, state, config) + ) +} + +pragma[nomagic] +private predicate sourceNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSource(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSource(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +pragma[nomagic] +private predicate sinkNode(NodeEx node, FlowState state, Configuration config) { + ( + config.isSink(node.asNode()) and state instanceof FlowStateEmpty + or + config.isSink(node.asNode(), state) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) +} + +/** Provides the relevant barriers for a step from `node1` to `node2`. */ +pragma[inline] +private predicate stepFilter(NodeEx node1, NodeEx node2, Configuration config) { + not outBarrier(node1, config) and + not inBarrier(node2, config) and + not fullBarrier(node1, config) and + not fullBarrier(node2, config) +} + +/** + * Holds if data can flow in one local step from `node1` to `node2`. + */ +private predicate localFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + simpleLocalFlowStepExt(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.asNode() = n and + node2.isImplicitReadNode(n, false) and + not fullBarrier(node1, config) + ) +} + +/** + * Holds if the additional step from `node1` to `node2` does not jump between callables. + */ +private predicate additionalLocalFlowStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) + ) + or + exists(Node n | + config.allowImplicitRead(n, _) and + node1.isImplicitReadNode(n, true) and + node2.asNode() = n and + not fullBarrier(node2, config) + ) +} + +private predicate additionalLocalStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) = getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) + ) +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. + */ +private predicate jumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + jumpStepCached(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +/** + * Holds if the additional step from `node1` to `node2` jumps between callables. + */ +private predicate additionalJumpStep(NodeEx node1, NodeEx node2, Configuration config) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), pragma[only_bind_into](n2)) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate additionalJumpStateStep( + NodeEx node1, FlowState s1, NodeEx node2, FlowState s2, Configuration config +) { + exists(Node n1, Node n2 | + node1.asNode() = n1 and + node2.asNode() = n2 and + config.isAdditionalFlowStep(pragma[only_bind_into](n1), s1, pragma[only_bind_into](n2), s2) and + getNodeEnclosingCallable(n1) != getNodeEnclosingCallable(n2) and + stepFilter(node1, node2, config) and + not stateBarrier(node1, s1, config) and + not stateBarrier(node2, s2, config) and + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) +} + +pragma[nomagic] +private predicate readSet(NodeEx node1, ContentSet c, NodeEx node2, Configuration config) { + readSet(pragma[only_bind_into](node1.asNode()), c, pragma[only_bind_into](node2.asNode())) and + stepFilter(node1, node2, config) + or + exists(Node n | + node2.isImplicitReadNode(n, true) and + node1.isImplicitReadNode(n, _) and + config.allowImplicitRead(n, c) + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate read(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(ContentSet cs | + readSet(node1, cs, node2, config) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate clearsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + clearsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +// inline to reduce fan-out via `getAReadContent` +bindingset[c] +private predicate expectsContentEx(NodeEx n, Content c) { + exists(ContentSet cs | + expectsContentCached(n.asNode(), cs) and + pragma[only_bind_out](c) = pragma[only_bind_into](cs).getAReadContent() + ) +} + +pragma[nomagic] +private predicate notExpectsContent(NodeEx n) { not expectsContentCached(n.asNode(), _) } + +pragma[nomagic] +private predicate store( + NodeEx node1, TypedContent tc, NodeEx node2, DataFlowType contentType, Configuration config +) { + store(pragma[only_bind_into](node1.asNode()), tc, pragma[only_bind_into](node2.asNode()), + contentType) and + read(_, tc.getContent(), _, config) and + stepFilter(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutEx(DataFlowCall call, ReturnPosition pos, NodeEx out) { + viableReturnPosOut(call, pos, out.asNode()) +} + +pragma[nomagic] +private predicate viableParamArgEx(DataFlowCall call, ParamNodeEx p, ArgNodeEx arg) { + viableParamArg(call, p.asNode(), arg.asNode()) +} + +/** + * Holds if field flow should be used for the given configuration. + */ +private predicate useFieldFlow(Configuration config) { config.fieldFlowBranchLimit() >= 1 } + +private predicate hasSourceCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSourceCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private predicate hasSinkCallCtx(Configuration config) { + exists(FlowFeature feature | feature = config.getAFeature() | + feature instanceof FeatureHasSinkCallContext or + feature instanceof FeatureEqualSourceSinkCallContext + ) +} + +private module Stage1 implements StageSig { + class ApApprox = Unit; + + class Ap = Unit; + + class ApOption = Unit; + + class Cc = boolean; + + /* Begin: Stage 1 logic. */ + /** + * Holds if `node` is reachable from a source in the configuration `config`. + * + * The Boolean `cc` records whether the node is reached through an + * argument in a call. + */ + predicate fwdFlow(NodeEx node, Cc cc, Configuration config) { + sourceNode(node, _, config) and + if hasSourceCallCtx(config) then cc = true else cc = false + or + exists(NodeEx mid | fwdFlow(mid, cc, config) | + localFlowStep(mid, node, config) or + additionalLocalFlowStep(mid, node, config) or + additionalLocalStateStep(mid, _, node, _, config) + ) + or + exists(NodeEx mid | fwdFlow(mid, _, config) and cc = false | + jumpStep(mid, node, config) or + additionalJumpStep(mid, node, config) or + additionalJumpStateStep(mid, _, node, _, config) + ) + or + // store + exists(NodeEx mid | + useFieldFlow(config) and + fwdFlow(mid, cc, config) and + store(mid, _, node, _, config) + ) + or + // read + exists(ContentSet c | + fwdFlowReadSet(c, node, cc, config) and + fwdFlowConsCandSet(c, _, config) + ) + or + // flow into a callable + exists(NodeEx arg | + fwdFlow(arg, _, config) and + viableParamArgEx(_, node, arg) and + cc = true and + not fullBarrier(node, config) + ) + or + // flow out of a callable + exists(DataFlowCall call | + fwdFlowOut(call, node, false, config) and + cc = false + or + fwdFlowOutFromArg(call, node, config) and + fwdFlowIsEntered(call, cc, config) + ) + } + + private predicate fwdFlow(NodeEx node, Configuration config) { fwdFlow(node, _, config) } + + pragma[nomagic] + private predicate fwdFlowReadSet(ContentSet c, NodeEx node, Cc cc, Configuration config) { + exists(NodeEx mid | + fwdFlow(mid, cc, config) and + readSet(mid, c, node, config) + ) + } + + /** + * Holds if `c` is the target of a store in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, TypedContent tc | + not fullBarrier(node, config) and + useFieldFlow(config) and + fwdFlow(mid, _, config) and + store(mid, tc, node, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `cs` may be interpreted in a read as the target of some store + * into `c`, in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowConsCandSet(ContentSet cs, Content c, Configuration config) { + fwdFlowConsCand(c, config) and + c = cs.getAReadContent() + } + + pragma[nomagic] + private predicate fwdFlowReturnPosition(ReturnPosition pos, Cc cc, Configuration config) { + exists(RetNodeEx ret | + fwdFlow(ret, cc, config) and + ret.getReturnPosition() = pos + ) + } + + pragma[nomagic] + private predicate fwdFlowOut(DataFlowCall call, NodeEx out, Cc cc, Configuration config) { + exists(ReturnPosition pos | + fwdFlowReturnPosition(pos, cc, config) and + viableReturnPosOutEx(call, pos, out) and + not fullBarrier(out, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg(DataFlowCall call, NodeEx out, Configuration config) { + fwdFlowOut(call, out, true, config) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered(DataFlowCall call, Cc cc, Configuration config) { + exists(ArgNodeEx arg | + fwdFlow(arg, cc, config) and + viableParamArgEx(call, _, arg) + ) + } + + private predicate stateStepFwd(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1 | + additionalLocalStateStep(node1, state1, _, state2, config) or + additionalJumpStateStep(node1, state1, _, state2, config) + | + fwdFlow(node1, config) + ) + } + + private predicate fwdFlowState(FlowState state, Configuration config) { + sourceNode(_, state, config) + or + exists(FlowState state0 | + fwdFlowState(state0, config) and + stateStepFwd(state0, state, config) + ) + } + + /** + * Holds if `node` is part of a path from a source to a sink in the + * configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from + * the enclosing callable in order to reach a sink. + */ + pragma[nomagic] + predicate revFlow(NodeEx node, boolean toReturn, Configuration config) { + revFlow0(node, toReturn, config) and + fwdFlow(node, config) + } + + pragma[nomagic] + private predicate revFlow0(NodeEx node, boolean toReturn, Configuration config) { + exists(FlowState state | + fwdFlow(node, pragma[only_bind_into](config)) and + sinkNode(node, state, config) and + fwdFlowState(state, pragma[only_bind_into](config)) and + if hasSinkCallCtx(config) then toReturn = true else toReturn = false + ) + or + exists(NodeEx mid | revFlow(mid, toReturn, config) | + localFlowStep(node, mid, config) or + additionalLocalFlowStep(node, mid, config) or + additionalLocalStateStep(node, _, mid, _, config) + ) + or + exists(NodeEx mid | revFlow(mid, _, config) and toReturn = false | + jumpStep(node, mid, config) or + additionalJumpStep(node, mid, config) or + additionalJumpStateStep(node, _, mid, _, config) + ) + or + // store + exists(Content c | + revFlowStore(c, node, toReturn, config) and + revFlowConsCand(c, config) + ) + or + // read + exists(NodeEx mid, ContentSet c | + readSet(node, c, mid, config) and + fwdFlowConsCandSet(c, _, pragma[only_bind_into](config)) and + revFlow(mid, toReturn, pragma[only_bind_into](config)) + ) + or + // flow into a callable + exists(DataFlowCall call | + revFlowIn(call, node, false, config) and + toReturn = false + or + revFlowInToReturn(call, node, config) and + revFlowIsReturned(call, toReturn, config) + ) + or + // flow out of a callable + exists(ReturnPosition pos | + revFlowOut(pos, config) and + node.(RetNodeEx).getReturnPosition() = pos and + toReturn = true + ) + } + + /** + * Holds if `c` is the target of a read in the flow covered by `revFlow`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Content c, Configuration config) { + exists(NodeEx mid, NodeEx node, ContentSet cs | + fwdFlow(node, pragma[only_bind_into](config)) and + readSet(node, cs, mid, config) and + fwdFlowConsCandSet(cs, c, pragma[only_bind_into](config)) and + revFlow(pragma[only_bind_into](mid), _, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate revFlowStore(Content c, NodeEx node, boolean toReturn, Configuration config) { + exists(NodeEx mid, TypedContent tc | + revFlow(mid, toReturn, pragma[only_bind_into](config)) and + fwdFlowConsCand(c, pragma[only_bind_into](config)) and + store(node, tc, mid, _, config) and + c = tc.getContent() + ) + } + + /** + * Holds if `c` is the target of both a read and a store in the flow covered + * by `revFlow`. + */ + pragma[nomagic] + predicate revFlowIsReadAndStored(Content c, Configuration conf) { + revFlowConsCand(c, conf) and + revFlowStore(c, _, _, conf) + } + + pragma[nomagic] + predicate viableReturnPosOutNodeCandFwd1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config + ) { + fwdFlowReturnPosition(pos, _, config) and + viableReturnPosOutEx(call, pos, out) + } + + pragma[nomagic] + private predicate revFlowOut(ReturnPosition pos, Configuration config) { + exists(DataFlowCall call, NodeEx out | + revFlow(out, _, config) and + viableReturnPosOutNodeCandFwd1(call, pos, out, config) + ) + } + + pragma[nomagic] + predicate viableParamArgNodeCandFwd1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config + ) { + viableParamArgEx(call, p, arg) and + fwdFlow(arg, config) + } + + pragma[nomagic] + private predicate revFlowIn( + DataFlowCall call, ArgNodeEx arg, boolean toReturn, Configuration config + ) { + exists(ParamNodeEx p | + revFlow(p, toReturn, config) and + viableParamArgNodeCandFwd1(call, p, arg, config) + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn(DataFlowCall call, ArgNodeEx arg, Configuration config) { + revFlowIn(call, arg, true, config) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned(DataFlowCall call, boolean toReturn, Configuration config) { + exists(NodeEx out | + revFlow(out, toReturn, config) and + fwdFlowOutFromArg(call, out, config) + ) + } + + private predicate stateStepRev(FlowState state1, FlowState state2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + additionalLocalStateStep(node1, state1, node2, state2, config) or + additionalJumpStateStep(node1, state1, node2, state2, config) + | + revFlow(node1, _, pragma[only_bind_into](config)) and + revFlow(node2, _, pragma[only_bind_into](config)) and + fwdFlowState(state1, pragma[only_bind_into](config)) and + fwdFlowState(state2, pragma[only_bind_into](config)) + ) + } + + predicate revFlowState(FlowState state, Configuration config) { + exists(NodeEx node | + sinkNode(node, state, config) and + revFlow(node, _, pragma[only_bind_into](config)) and + fwdFlowState(state, pragma[only_bind_into](config)) + ) + or + exists(FlowState state0 | + revFlowState(state0, config) and + stateStepRev(state, state0, config) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Content c | + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + revFlow(node2, pragma[only_bind_into](config)) and + store(node1, tc, node2, contentType, config) and + c = tc.getContent() and + exists(ap1) + ) + } + + pragma[nomagic] + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config) { + revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + read(n1, c, n2, pragma[only_bind_into](config)) and + revFlow(n2, pragma[only_bind_into](config)) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, config) } + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, _, pragma[only_bind_into](config)) and + exists(state) and + exists(ap) + } + + private predicate throughFlowNodeCand(NodeEx node, Configuration config) { + revFlow(node, true, config) and + fwdFlow(node, true, config) and + not inBarrier(node, config) and + not outBarrier(node, config) + } + + /** Holds if flow may return from `callable`. */ + pragma[nomagic] + private predicate returnFlowCallableNodeCand( + DataFlowCallable callable, ReturnKindExt kind, Configuration config + ) { + exists(RetNodeEx ret | + throughFlowNodeCand(ret, config) and + callable = ret.getEnclosingCallable() and + kind = ret.getKind() + ) + } + + /** + * Holds if flow may enter through `p` and reach a return node making `p` a + * candidate for the origin of a summary. + */ + pragma[nomagic] + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(ReturnKindExt kind | + throughFlowNodeCand(p, config) and + returnFlowCallableNodeCand(c, kind, config) and + p.getEnclosingCallable() = c and + exists(ap) and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = p.getPosition() + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists(ArgNodeEx arg, boolean toReturn | + revFlow(arg, toReturn, config) and + revFlowInToReturn(call, arg, config) and + revFlowIsReturned(call, toReturn, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, config)) and + fields = count(Content f0 | fwdFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | fwdFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | fwdFlow(n, b, config)) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, config)) and + fields = count(Content f0 | revFlowConsCand(f0, config)) and + conscand = -1 and + states = count(FlowState state | revFlowState(state, config)) and + tuples = count(NodeEx n, boolean b | revFlow(n, b, config)) + } + /* End: Stage 1 logic. */ +} + +pragma[noinline] +private predicate localFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + localFlowStep(node1, node2, config) +} + +pragma[noinline] +private predicate additionalLocalFlowStepNodeCand1(NodeEx node1, NodeEx node2, Configuration config) { + Stage1::revFlow(node2, config) and + additionalLocalFlowStep(node1, node2, config) +} + +pragma[nomagic] +private predicate viableReturnPosOutNodeCand1( + DataFlowCall call, ReturnPosition pos, NodeEx out, Configuration config +) { + Stage1::revFlow(out, config) and + Stage1::viableReturnPosOutNodeCandFwd1(call, pos, out, config) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, Configuration config +) { + viableReturnPosOutNodeCand1(call, ret.getReturnPosition(), out, config) and + Stage1::revFlow(ret, config) and + not outBarrier(ret, config) and + not inBarrier(out, config) +} + +pragma[nomagic] +private predicate viableParamArgNodeCand1( + DataFlowCall call, ParamNodeEx p, ArgNodeEx arg, Configuration config +) { + Stage1::viableParamArgNodeCandFwd1(call, p, arg, config) and + Stage1::revFlow(arg, config) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, Configuration config +) { + viableParamArgNodeCand1(call, p, arg, config) and + Stage1::revFlow(p, config) and + not outBarrier(arg, config) and + not inBarrier(p, config) +} + +/** + * Gets the amount of forward branching on the origin of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int branch(NodeEx n1, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n1, n, conf) or flowIntoCallNodeCand1(_, n1, n, conf) + ) +} + +/** + * Gets the amount of backward branching on the target of a cross-call path + * edge in the graph of paths between sources and sinks that ignores call + * contexts. + */ +private int join(NodeEx n2, Configuration conf) { + result = + strictcount(NodeEx n | + flowOutOfCallNodeCand1(_, n, n2, conf) or flowIntoCallNodeCand1(_, n, n2, conf) + ) +} + +/** + * Holds if data can flow out of `call` from `ret` to `out`, either + * through a `ReturnNode` or through an argument that has been mutated, and + * that this step is part of a path from a source to a sink. The + * `allowsFieldFlow` flag indicates whether the branching is within the limit + * specified by the configuration. + */ +pragma[nomagic] +private predicate flowOutOfCallNodeCand1( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, ret, out, config) and + exists(int b, int j | + b = branch(ret, config) and + j = join(out, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +/** + * Holds if data can flow into `call` and that this step is part of a + * path from a source to a sink. The `allowsFieldFlow` flag indicates whether + * the branching is within the limit specified by the configuration. + */ +pragma[nomagic] +private predicate flowIntoCallNodeCand1( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config +) { + flowIntoCallNodeCand1(call, arg, p, config) and + exists(int b, int j | + b = branch(arg, config) and + j = join(p, config) and + if b.minimum(j) <= config.fieldFlowBranchLimit() + then allowsFieldFlow = true + else allowsFieldFlow = false + ) +} + +private signature module StageSig { + class Ap; + + predicate revFlow(NodeEx node, Configuration config); + + bindingset[node, state, config] + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config); + + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config); + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config); + + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ); + + predicate readStepCand(NodeEx n1, Content c, NodeEx n2, Configuration config); +} + +private module MkStage { + class ApApprox = PrevStage::Ap; + + signature module StageParam { + class Ap; + + class ApNil extends Ap; + + bindingset[result, ap] + ApApprox getApprox(Ap ap); + + ApNil getApNil(NodeEx node); + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail); + + Content getHeadContent(Ap ap); + + class ApOption; + + ApOption apNone(); + + ApOption apSome(Ap ap); + + class Cc; + + class CcCall extends Cc; + + // TODO: member predicate on CcCall + predicate matchesCall(CcCall cc, DataFlowCall call); + + class CcNoCall extends Cc; + + Cc ccNone(); + + CcCall ccSomeCall(); + + class LocalCc; + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc); + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc); + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc); + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ); + + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, Configuration config + ); + + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ); + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config); + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType); + } + + module Stage implements StageSig { + import Param + + /* Begin: Stage logic. */ + bindingset[result, apa] + private ApApprox unbindApa(ApApprox apa) { + pragma[only_bind_out](apa) = pragma[only_bind_out](result) + } + + pragma[nomagic] + private predicate flowThroughOutOfCall( + DataFlowCall call, CcCall ccc, RetNodeEx ret, NodeEx out, boolean allowsFieldFlow, + Configuration config + ) { + flowOutOfCall(call, ret, out, allowsFieldFlow, pragma[only_bind_into](config)) and + PrevStage::callMayFlowThroughRev(call, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(_, ret.getEnclosingCallable(), _, + pragma[only_bind_into](config)) and + matchesCall(ccc, call) + } + + /** + * Holds if `node` is reachable with access path `ap` from a source in the + * configuration `config`. + * + * The call context `cc` records whether the node is reached through an + * argument in a call, and if so, `argAp` records the access path of that + * argument. + */ + pragma[nomagic] + predicate fwdFlow( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + fwdFlow0(node, state, cc, argAp, ap, config) and + PrevStage::revFlow(node, state, unbindApa(getApprox(ap)), config) and + filter(node, state, ap, config) + } + + pragma[nomagic] + private predicate fwdFlow0( + NodeEx node, FlowState state, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + sourceNode(node, state, config) and + (if hasSourceCallCtx(config) then cc = ccSomeCall() else cc = ccNone()) and + argAp = apNone() and + ap = getApNil(node) + or + exists(NodeEx mid, FlowState state0, Ap ap0, LocalCc localCc | + fwdFlow(mid, state0, cc, argAp, ap0, config) and + localCc = getLocalCc(mid, cc) + | + localStep(mid, state0, node, state, true, _, config, localCc) and + ap = ap0 + or + localStep(mid, state0, node, state, false, ap, config, localCc) and + ap0 instanceof ApNil + ) + or + exists(NodeEx mid | + fwdFlow(mid, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + jumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(mid, state, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStep(mid, node, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(mid, state0, _, _, nil, pragma[only_bind_into](config)) and + additionalJumpStateStep(mid, state0, node, state, config) and + cc = ccNone() and + argAp = apNone() and + ap = getApNil(node) + ) + or + // store + exists(TypedContent tc, Ap ap0 | + fwdFlowStore(_, ap0, tc, node, state, cc, argAp, config) and + ap = apCons(tc, ap0) + ) + or + // read + exists(Ap ap0, Content c | + fwdFlowRead(ap0, c, _, node, state, cc, argAp, config) and + fwdFlowConsCand(ap0, c, ap, config) + ) + or + // flow into a callable + exists(ApApprox apa | + fwdFlowIn(_, node, state, _, cc, _, ap, config) and + apa = getApprox(ap) and + if PrevStage::parameterMayFlowThrough(node, _, apa, config) + then argAp = apSome(ap) + else argAp = apNone() + ) + or + // flow out of a callable + fwdFlowOutNotFromArg(node, state, cc, argAp, ap, config) + or + exists(DataFlowCall call, Ap argAp0 | + fwdFlowOutFromArg(call, node, state, argAp0, ap, config) and + fwdFlowIsEntered(call, cc, argAp, argAp0, config) + ) + } + + pragma[nomagic] + private predicate fwdFlowStore( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + exists(DataFlowType contentType | + fwdFlow(node1, state, cc, argAp, ap1, config) and + PrevStage::storeStepCand(node1, unbindApa(getApprox(ap1)), tc, node2, contentType, config) and + typecheckStore(ap1, contentType) + ) + } + + /** + * Holds if forward flow with access path `tail` reaches a store of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate fwdFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(TypedContent tc | + fwdFlowStore(_, tail, tc, _, _, _, _, config) and + tc.getContent() = c and + cons = apCons(tc, tail) + ) + } + + pragma[nomagic] + private predicate fwdFlowRead( + Ap ap, Content c, NodeEx node1, NodeEx node2, FlowState state, Cc cc, ApOption argAp, + Configuration config + ) { + fwdFlow(node1, state, cc, argAp, ap, config) and + PrevStage::readStepCand(node1, c, node2, config) and + getHeadContent(ap) = c + } + + pragma[nomagic] + private predicate fwdFlowIn( + DataFlowCall call, ParamNodeEx p, FlowState state, Cc outercc, Cc innercc, ApOption argAp, + Ap ap, Configuration config + ) { + exists(ArgNodeEx arg, boolean allowsFieldFlow | + fwdFlow(arg, state, outercc, argAp, ap, config) and + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + innercc = getCallContextCall(call, p.getEnclosingCallable(), outercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutNotFromArg( + NodeEx out, FlowState state, Cc ccOut, ApOption argAp, Ap ap, Configuration config + ) { + exists( + DataFlowCall call, RetNodeEx ret, boolean allowsFieldFlow, CcNoCall innercc, + DataFlowCallable inner + | + fwdFlow(ret, state, innercc, argAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + inner = ret.getEnclosingCallable() and + ccOut = getCallContextReturn(inner, call, innercc) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate fwdFlowOutFromArg( + DataFlowCall call, NodeEx out, FlowState state, Ap argAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, boolean allowsFieldFlow, CcCall ccc | + fwdFlow(ret, state, ccc, apSome(argAp), ap, config) and + flowThroughOutOfCall(call, ccc, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an argument to `call` is reached in the flow covered by `fwdFlow` + * and data might flow through the target callable and back out at `call`. + */ + pragma[nomagic] + private predicate fwdFlowIsEntered( + DataFlowCall call, Cc cc, ApOption argAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p | + fwdFlowIn(call, p, _, cc, _, argAp, ap, config) and + PrevStage::parameterMayFlowThrough(p, _, unbindApa(getApprox(ap)), config) + ) + } + + pragma[nomagic] + private predicate storeStepFwd( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, Ap ap2, Configuration config + ) { + fwdFlowStore(node1, ap1, tc, node2, _, _, _, config) and + ap2 = apCons(tc, ap1) and + fwdFlowRead(ap2, tc.getContent(), _, _, _, _, _, config) + } + + private predicate readStepFwd( + NodeEx n1, Ap ap1, Content c, NodeEx n2, Ap ap2, Configuration config + ) { + fwdFlowRead(ap1, c, n1, n2, _, _, _, config) and + fwdFlowConsCand(ap1, c, ap2, config) + } + + pragma[nomagic] + private predicate callMayFlowThroughFwd(DataFlowCall call, Configuration config) { + exists(Ap argAp0, NodeEx out, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(out, state, pragma[only_bind_into](cc), pragma[only_bind_into](argAp), ap, + pragma[only_bind_into](config)) and + fwdFlowOutFromArg(call, out, state, argAp0, ap, config) and + fwdFlowIsEntered(pragma[only_bind_into](call), pragma[only_bind_into](cc), + pragma[only_bind_into](argAp), pragma[only_bind_into](argAp0), + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate flowThroughIntoCall( + DataFlowCall call, ArgNodeEx arg, ParamNodeEx p, boolean allowsFieldFlow, Configuration config + ) { + flowIntoCall(call, arg, p, allowsFieldFlow, config) and + fwdFlow(arg, _, _, _, _, pragma[only_bind_into](config)) and + PrevStage::parameterMayFlowThrough(p, _, _, pragma[only_bind_into](config)) and + callMayFlowThroughFwd(call, pragma[only_bind_into](config)) + } + + pragma[nomagic] + private predicate returnNodeMayFlowThrough( + RetNodeEx ret, FlowState state, Ap ap, Configuration config + ) { + fwdFlow(ret, state, any(CcCall ccc), apSome(_), ap, config) + } + + /** + * Holds if `node` with access path `ap` is part of a path from a source to a + * sink in the configuration `config`. + * + * The Boolean `toReturn` records whether the node must be returned from the + * enclosing callable in order to reach a sink, and if so, `returnAp` records + * the access path of the returned value. + */ + pragma[nomagic] + predicate revFlow( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + revFlow0(node, state, toReturn, returnAp, ap, config) and + fwdFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + private predicate revFlow0( + NodeEx node, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + fwdFlow(node, state, _, _, ap, config) and + sinkNode(node, state, config) and + (if hasSinkCallCtx(config) then toReturn = true else toReturn = false) and + returnAp = apNone() and + ap instanceof ApNil + or + exists(NodeEx mid, FlowState state0 | + localStep(node, state, mid, state0, true, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, ap, config) + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, pragma[only_bind_into](state), _, _, ap, pragma[only_bind_into](config)) and + localStep(node, pragma[only_bind_into](state), mid, state0, false, _, config, _) and + revFlow(mid, state0, toReturn, returnAp, nil, pragma[only_bind_into](config)) and + ap instanceof ApNil + ) + or + exists(NodeEx mid | + jumpStep(node, mid, config) and + revFlow(mid, state, _, _, ap, config) and + toReturn = false and + returnAp = apNone() + ) + or + exists(NodeEx mid, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStep(node, mid, config) and + revFlow(pragma[only_bind_into](mid), state, _, _, nil, pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + exists(NodeEx mid, FlowState state0, ApNil nil | + fwdFlow(node, _, _, _, ap, pragma[only_bind_into](config)) and + additionalJumpStateStep(node, state, mid, state0, config) and + revFlow(pragma[only_bind_into](mid), pragma[only_bind_into](state0), _, _, nil, + pragma[only_bind_into](config)) and + toReturn = false and + returnAp = apNone() and + ap instanceof ApNil + ) + or + // store + exists(Ap ap0, Content c | + revFlowStore(ap0, c, ap, node, state, _, _, toReturn, returnAp, config) and + revFlowConsCand(ap0, c, ap, config) + ) + or + // read + exists(NodeEx mid, Ap ap0 | + revFlow(mid, state, toReturn, returnAp, ap0, config) and + readStepFwd(node, ap, _, mid, ap0, config) + ) + or + // flow into a callable + revFlowInNotToReturn(node, state, returnAp, ap, config) and + toReturn = false + or + exists(DataFlowCall call, Ap returnAp0 | + revFlowInToReturn(call, node, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + or + // flow out of a callable + revFlowOut(_, node, state, _, _, ap, config) and + toReturn = true and + if returnNodeMayFlowThrough(node, state, ap, config) + then returnAp = apSome(ap) + else returnAp = apNone() + } + + pragma[nomagic] + private predicate revFlowStore( + Ap ap0, Content c, Ap ap, NodeEx node, FlowState state, TypedContent tc, NodeEx mid, + boolean toReturn, ApOption returnAp, Configuration config + ) { + revFlow(mid, state, toReturn, returnAp, ap0, config) and + storeStepFwd(node, ap, tc, mid, ap0, config) and + tc.getContent() = c + } + + /** + * Holds if reverse flow with access path `tail` reaches a read of `c` + * resulting in access path `cons`. + */ + pragma[nomagic] + private predicate revFlowConsCand(Ap cons, Content c, Ap tail, Configuration config) { + exists(NodeEx mid, Ap tail0 | + revFlow(mid, _, _, _, tail, config) and + tail = pragma[only_bind_into](tail0) and + readStepFwd(_, cons, c, mid, tail0, config) + ) + } + + pragma[nomagic] + private predicate revFlowOut( + DataFlowCall call, RetNodeEx ret, FlowState state, boolean toReturn, ApOption returnAp, Ap ap, + Configuration config + ) { + exists(NodeEx out, boolean allowsFieldFlow | + revFlow(out, state, toReturn, returnAp, ap, config) and + flowOutOfCall(call, ret, out, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInNotToReturn( + ArgNodeEx arg, FlowState state, ApOption returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, false, returnAp, ap, config) and + flowIntoCall(_, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + pragma[nomagic] + private predicate revFlowInToReturn( + DataFlowCall call, ArgNodeEx arg, FlowState state, Ap returnAp, Ap ap, Configuration config + ) { + exists(ParamNodeEx p, boolean allowsFieldFlow | + revFlow(p, state, true, apSome(returnAp), ap, config) and + flowThroughIntoCall(call, arg, p, allowsFieldFlow, config) and + if allowsFieldFlow = false then ap instanceof ApNil else any() + ) + } + + /** + * Holds if an output from `call` is reached in the flow covered by `revFlow` + * and data might flow through the target callable resulting in reverse flow + * reaching an argument of `call`. + */ + pragma[nomagic] + private predicate revFlowIsReturned( + DataFlowCall call, boolean toReturn, ApOption returnAp, Ap ap, Configuration config + ) { + exists(RetNodeEx ret, FlowState state, CcCall ccc | + revFlowOut(call, ret, state, toReturn, returnAp, ap, config) and + fwdFlow(ret, state, ccc, apSome(_), ap, config) and + matchesCall(ccc, call) + ) + } + + pragma[nomagic] + predicate storeStepCand( + NodeEx node1, Ap ap1, TypedContent tc, NodeEx node2, DataFlowType contentType, + Configuration config + ) { + exists(Ap ap2, Content c | + PrevStage::storeStepCand(node1, _, tc, node2, contentType, config) and + revFlowStore(ap2, c, ap1, node1, _, tc, node2, _, _, config) and + revFlowConsCand(ap2, c, ap1, config) + ) + } + + predicate readStepCand(NodeEx node1, Content c, NodeEx node2, Configuration config) { + exists(Ap ap1, Ap ap2 | + revFlow(node2, _, _, _, pragma[only_bind_into](ap2), pragma[only_bind_into](config)) and + readStepFwd(node1, ap1, c, node2, ap2, config) and + revFlowStore(ap1, c, pragma[only_bind_into](ap2), _, _, _, _, _, _, + pragma[only_bind_into](config)) + ) + } + + predicate revFlow(NodeEx node, FlowState state, Configuration config) { + revFlow(node, state, _, _, _, config) + } + + predicate revFlow(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, _, _, ap, config) + } + + pragma[nomagic] + predicate revFlow(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, Configuration config) { revFlow(node, _, _, _, _, config) } + + // use an alias as a workaround for bad functionality-induced joins + pragma[nomagic] + predicate revFlowAlias(NodeEx node, FlowState state, Ap ap, Configuration config) { + revFlow(node, state, ap, config) + } + + private predicate fwdConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepFwd(_, ap, tc, _, _, config) + } + + private predicate revConsCand(TypedContent tc, Ap ap, Configuration config) { + storeStepCand(_, ap, tc, _, _, config) + } + + private predicate validAp(Ap ap, Configuration config) { + revFlow(_, _, _, _, ap, config) and ap instanceof ApNil + or + exists(TypedContent head, Ap tail | + consCand(head, tail, config) and + ap = apCons(head, tail) + ) + } + + predicate consCand(TypedContent tc, Ap ap, Configuration config) { + revConsCand(tc, ap, config) and + validAp(ap, config) + } + + pragma[noinline] + private predicate parameterFlow( + ParamNodeEx p, Ap ap, Ap ap0, DataFlowCallable c, Configuration config + ) { + revFlow(p, _, true, apSome(ap0), ap, config) and + c = p.getEnclosingCallable() + } + + predicate parameterMayFlowThrough(ParamNodeEx p, DataFlowCallable c, Ap ap, Configuration config) { + exists(RetNodeEx ret, FlowState state, Ap ap0, ReturnKindExt kind, ParameterPosition pos | + parameterFlow(p, ap, ap0, c, config) and + c = ret.getEnclosingCallable() and + revFlow(pragma[only_bind_into](ret), pragma[only_bind_into](state), true, apSome(_), + pragma[only_bind_into](ap0), pragma[only_bind_into](config)) and + fwdFlow(ret, state, any(CcCall ccc), apSome(ap), ap0, config) and + kind = ret.getKind() and + p.getPosition() = pos and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + p.allowParameterReturnInSelf() + ) + ) + } + + pragma[nomagic] + predicate callMayFlowThroughRev(DataFlowCall call, Configuration config) { + exists( + Ap returnAp0, ArgNodeEx arg, FlowState state, boolean toReturn, ApOption returnAp, Ap ap + | + revFlow(arg, state, toReturn, returnAp, ap, config) and + revFlowInToReturn(call, arg, state, returnAp0, ap, config) and + revFlowIsReturned(call, toReturn, returnAp, returnAp0, config) + ) + } + + predicate stats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples, Configuration config + ) { + fwd = true and + nodes = count(NodeEx node | fwdFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | fwdConsCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | fwdConsCand(f0, ap, config)) and + states = count(FlowState state | fwdFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, Cc cc, ApOption argAp, Ap ap | + fwdFlow(n, state, cc, argAp, ap, config) + ) + or + fwd = false and + nodes = count(NodeEx node | revFlow(node, _, _, _, _, config)) and + fields = count(TypedContent f0 | consCand(f0, _, config)) and + conscand = count(TypedContent f0, Ap ap | consCand(f0, ap, config)) and + states = count(FlowState state | revFlow(_, state, _, _, _, config)) and + tuples = + count(NodeEx n, FlowState state, boolean b, ApOption retAp, Ap ap | + revFlow(n, state, b, retAp, ap, config) + ) + } + /* End: Stage logic. */ + } +} + +private module BooleanCallContext { + class Cc extends boolean { + Cc() { this in [true, false] } + } + + class CcCall extends Cc { + CcCall() { this = true } + } + + /** Holds if the call context may be `call`. */ + predicate matchesCall(CcCall cc, DataFlowCall call) { any() } + + class CcNoCall extends Cc { + CcNoCall() { this = false } + } + + Cc ccNone() { result = false } + + CcCall ccSomeCall() { result = true } + + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { any() } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { any() } +} + +private module Level1CallContext { + class Cc = CallContext; + + class CcCall = CallContextCall; + + pragma[inline] + predicate matchesCall(CcCall cc, DataFlowCall call) { cc.matchesCall(call) } + + class CcNoCall = CallContextNoCall; + + Cc ccNone() { result instanceof CallContextAny } + + CcCall ccSomeCall() { result instanceof CallContextSomeCall } + + module NoLocalCallContext { + class LocalCc = Unit; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { any() } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSiteDispatch(call, c) + then result = TSpecificCall(call) + else result = TSomeCall() + } + } + + module LocalCallContext { + class LocalCc = LocalCallContext; + + bindingset[node, cc] + LocalCc getLocalCc(NodeEx node, Cc cc) { + result = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + node.getEnclosingCallable()) + } + + bindingset[call, c, outercc] + CcCall getCallContextCall(DataFlowCall call, DataFlowCallable c, Cc outercc) { + checkCallContextCall(outercc, call, c) and + if recordDataFlowCallSite(call, c) then result = TSpecificCall(call) else result = TSomeCall() + } + } + + bindingset[call, c, innercc] + CcNoCall getCallContextReturn(DataFlowCallable c, DataFlowCall call, Cc innercc) { + checkCallContextReturn(innercc, c, call) and + if reducedViableImplInReturn(c, call) then result = TReturn(c, call) else result = ccNone() + } +} + +private module Stage2Param implements MkStage::StageParam { + private module PrevStage = Stage1; + + class Ap extends boolean { + Ap() { this in [true, false] } + } + + class ApNil extends Ap { + ApNil() { this = false } + } + + bindingset[result, ap] + PrevStage::Ap getApprox(Ap ap) { any() } + + ApNil getApNil(NodeEx node) { Stage1::revFlow(node, _) and exists(result) } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = true and exists(tc) and exists(tail) } + + pragma[inline] + Content getHeadContent(Ap ap) { exists(result) and ap = true } + + class ApOption = BooleanOption; + + ApOption apNone() { result = TBooleanNone() } + + ApOption apSome(Ap ap) { result = TBooleanSome(ap) } + + import Level1CallContext + import NoLocalCallContext + + bindingset[node1, state1, config] + bindingset[node2, state2, config] + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + ( + preservesValue = true and + localFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 + or + preservesValue = false and + additionalLocalStateStep(node1, state1, node2, state2, config) + ) and + exists(ap) and + exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand1/5; + + predicate flowIntoCall = flowIntoCallNodeCand1/5; + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::revFlowIsReadAndStored(c, pragma[only_bind_into](config)) and + expectsContentEx(node, c) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + PrevStage::revFlowState(state, pragma[only_bind_into](config)) and + exists(ap) and + not stateBarrier(node, state, config) and + ( + notExpectsContent(node) + or + ap = true and + expectsContentCand(node, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage2 implements StageSig { + import MkStage::Stage +} + +pragma[nomagic] +private predicate flowOutOfCallNodeCand2( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config +) { + flowOutOfCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +pragma[nomagic] +private predicate flowIntoCallNodeCand2( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config +) { + flowIntoCallNodeCand1(call, node1, node2, allowsFieldFlow, config) and + Stage2::revFlow(node2, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node1, pragma[only_bind_into](config)) +} + +private module LocalFlowBigStep { + /** + * A node where some checking is required, and hence the big-step relation + * is not allowed to step over. + */ + private class FlowCheckNode extends NodeEx { + FlowCheckNode() { + castNode(this.asNode()) or + clearsContentCached(this.asNode(), _) or + expectsContentCached(this.asNode(), _) + } + } + + /** + * Holds if `node` can be the first node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowEntry(NodeEx node, FlowState state, Configuration config) { + Stage2::revFlow(node, state, config) and + ( + sourceNode(node, state, config) + or + jumpStep(_, node, config) + or + additionalJumpStep(_, node, config) + or + additionalJumpStateStep(_, _, node, state, config) + or + node instanceof ParamNodeEx + or + node.asNode() instanceof OutNodeExt + or + Stage2::storeStepCand(_, _, _, node, _, config) + or + Stage2::readStepCand(_, _, node, config) + or + node instanceof FlowCheckNode + or + exists(FlowState s | + additionalLocalStateStep(_, s, node, state, config) and + s != state + ) + ) + } + + /** + * Holds if `node` can be the last node in a maximal subsequence of local + * flow steps in a dataflow path. + */ + private predicate localFlowExit(NodeEx node, FlowState state, Configuration config) { + exists(NodeEx next | Stage2::revFlow(next, state, config) | + jumpStep(node, next, config) or + additionalJumpStep(node, next, config) or + flowIntoCallNodeCand1(_, node, next, config) or + flowOutOfCallNodeCand1(_, node, next, config) or + Stage2::storeStepCand(node, _, _, next, _, config) or + Stage2::readStepCand(node, _, next, config) + ) + or + exists(NodeEx next, FlowState s | Stage2::revFlow(next, s, config) | + additionalJumpStateStep(node, state, next, s, config) + or + additionalLocalStateStep(node, state, next, s, config) and + s != state + ) + or + Stage2::revFlow(node, state, config) and + node instanceof FlowCheckNode + or + sinkNode(node, state, config) + } + + pragma[noinline] + private predicate additionalLocalFlowStepNodeCand2( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, Configuration config + ) { + additionalLocalFlowStepNodeCand1(node1, node2, config) and + state1 = state2 and + Stage2::revFlow(node1, pragma[only_bind_into](state1), false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, pragma[only_bind_into](state2), false, + pragma[only_bind_into](config)) + or + additionalLocalStateStep(node1, state1, node2, state2, config) and + Stage2::revFlow(node1, state1, false, pragma[only_bind_into](config)) and + Stage2::revFlowAlias(node2, state2, false, pragma[only_bind_into](config)) + } + + /** + * Holds if the local path from `node1` to `node2` is a prefix of a maximal + * subsequence of local flow steps in a dataflow path. + * + * This is the transitive closure of `[additional]localFlowStep` beginning + * at `localFlowEntry`. + */ + pragma[nomagic] + private predicate localFlowStepPlus( + NodeEx node1, FlowState state, NodeEx node2, boolean preservesValue, DataFlowType t, + Configuration config, LocalCallContext cc + ) { + not isUnreachableInCallCached(node2.asNode(), cc.(LocalCallContextSpecificCall).getCall()) and + ( + localFlowEntry(node1, pragma[only_bind_into](state), pragma[only_bind_into](config)) and + ( + localFlowStepNodeCand1(node1, node2, config) and + preservesValue = true and + t = node1.getDataFlowType() and // irrelevant dummy value + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + or + additionalLocalFlowStepNodeCand2(node1, state, node2, state, config) and + preservesValue = false and + t = node2.getDataFlowType() + ) and + node1 != node2 and + cc.relevantFor(node1.getEnclosingCallable()) and + not isUnreachableInCallCached(node1.asNode(), cc.(LocalCallContextSpecificCall).getCall()) + or + exists(NodeEx mid | + localFlowStepPlus(node1, pragma[only_bind_into](state), mid, preservesValue, t, + pragma[only_bind_into](config), cc) and + localFlowStepNodeCand1(mid, node2, config) and + not mid instanceof FlowCheckNode and + Stage2::revFlow(node2, pragma[only_bind_into](state), pragma[only_bind_into](config)) + ) + or + exists(NodeEx mid | + localFlowStepPlus(node1, state, mid, _, _, pragma[only_bind_into](config), cc) and + additionalLocalFlowStepNodeCand2(mid, state, node2, state, config) and + not mid instanceof FlowCheckNode and + preservesValue = false and + t = node2.getDataFlowType() + ) + ) + } + + /** + * Holds if `node1` can step to `node2` in one or more local steps and this + * path can occur as a maximal subsequence of local steps in a dataflow path. + */ + pragma[nomagic] + predicate localFlowBigStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + AccessPathFrontNil apf, Configuration config, LocalCallContext callContext + ) { + localFlowStepPlus(node1, state1, node2, preservesValue, apf.getType(), config, callContext) and + localFlowExit(node2, state1, config) and + state1 = state2 + or + additionalLocalFlowStepNodeCand2(node1, state1, node2, state2, config) and + state1 != state2 and + preservesValue = false and + apf = TFrontNil(node2.getDataFlowType()) and + callContext.relevantFor(node1.getEnclosingCallable()) and + not exists(DataFlowCall call | call = callContext.(LocalCallContextSpecificCall).getCall() | + isUnreachableInCallCached(node1.asNode(), call) or + isUnreachableInCallCached(node2.asNode(), call) + ) + } +} + +private import LocalFlowBigStep + +private module Stage3Param implements MkStage::StageParam { + private module PrevStage = Stage2; + + class Ap = AccessPathFront; + + class ApNil = AccessPathFrontNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.toBoolNonEmpty() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TFrontNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result.getHead() = tc and exists(tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathFrontOption; + + ApOption apNone() { result = TAccessPathFrontNone() } + + ApOption apSome(Ap ap) { result = TAccessPathFrontSome(ap) } + + import BooleanCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap, config, _) and exists(lcc) + } + + predicate flowOutOfCall = flowOutOfCallNodeCand2/5; + + predicate flowIntoCall = flowIntoCallNodeCand2/5; + + pragma[nomagic] + private predicate clearSet(NodeEx node, ContentSet c, Configuration config) { + PrevStage::revFlow(node, config) and + clearsContentCached(node.asNode(), c) + } + + pragma[nomagic] + private predicate clearContent(NodeEx node, Content c, Configuration config) { + exists(ContentSet cs | + PrevStage::readStepCand(_, pragma[only_bind_into](c), _, pragma[only_bind_into](config)) and + c = cs.getAReadContent() and + clearSet(node, cs, pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + private predicate clear(NodeEx node, Ap ap, Configuration config) { + clearContent(node, ap.getHead().getContent(), config) + } + + pragma[nomagic] + private predicate expectsContentCand(NodeEx node, Ap ap, Configuration config) { + exists(Content c | + PrevStage::revFlow(node, pragma[only_bind_into](config)) and + PrevStage::readStepCand(_, c, _, pragma[only_bind_into](config)) and + expectsContentEx(node, c) and + c = ap.getHead().getContent() + ) + } + + pragma[nomagic] + private predicate castingNodeEx(NodeEx node) { node.asNode() instanceof CastingNode } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { + exists(state) and + exists(config) and + not clear(node, ap, config) and + (if castingNodeEx(node) then compatibleTypes(node.getDataFlowType(), ap.getType()) else any()) and + ( + notExpectsContent(node) + or + expectsContentCand(node, ap, config) + ) + } + + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { + // We need to typecheck stores here, since reverse flow through a getter + // might have a different type here compared to inside the getter. + compatibleTypes(ap.getType(), contentType) + } +} + +private module Stage3 implements StageSig { + import MkStage::Stage +} + +/** + * Holds if `argApf` is recorded as the summary context for flow reaching `node` + * and remains relevant for the following pruning stage. + */ +private predicate flowCandSummaryCtx( + NodeEx node, FlowState state, AccessPathFront argApf, Configuration config +) { + exists(AccessPathFront apf | + Stage3::revFlow(node, state, true, _, apf, config) and + Stage3::fwdFlow(node, state, any(Stage3::CcCall ccc), TAccessPathFrontSome(argApf), apf, config) + ) +} + +/** + * Holds if a length 2 access path approximation with the head `tc` is expected + * to be expensive. + */ +private predicate expensiveLen2unfolding(TypedContent tc, Configuration config) { + exists(int tails, int nodes, int apLimit, int tupleLimit | + tails = strictcount(AccessPathFront apf | Stage3::consCand(tc, apf, config)) and + nodes = + strictcount(NodeEx n, FlowState state | + Stage3::revFlow(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + or + flowCandSummaryCtx(n, state, any(AccessPathFrontHead apf | apf.getHead() = tc), config) + ) and + accessPathApproxCostLimits(apLimit, tupleLimit) and + apLimit < tails and + tupleLimit < (tails - 1) * nodes and + not tc.forceHighPrecision() + ) +} + +private newtype TAccessPathApprox = + TNil(DataFlowType t) or + TConsNil(TypedContent tc, DataFlowType t) { + Stage3::consCand(tc, TFrontNil(t), _) and + not expensiveLen2unfolding(tc, _) + } or + TConsCons(TypedContent tc1, TypedContent tc2, int len) { + Stage3::consCand(tc1, TFrontHead(tc2), _) and + len in [2 .. accessPathLimit()] and + not expensiveLen2unfolding(tc1, _) + } or + TCons1(TypedContent tc, int len) { + len in [1 .. accessPathLimit()] and + expensiveLen2unfolding(tc, _) + } + +/** + * Conceptually a list of `TypedContent`s followed by a `DataFlowType`, but only + * the first two elements of the list and its length are tracked. If data flows + * from a source to a given node with a given `AccessPathApprox`, this indicates + * the sequence of dereference operations needed to get from the value in the node + * to the tracked object. The final type indicates the type of the tracked object. + */ +abstract private class AccessPathApprox extends TAccessPathApprox { + abstract string toString(); + + abstract TypedContent getHead(); + + abstract int len(); + + abstract DataFlowType getType(); + + abstract AccessPathFront getFront(); + + /** Gets the access path obtained by popping `head` from this path, if any. */ + abstract AccessPathApprox pop(TypedContent head); +} + +private class AccessPathApproxNil extends AccessPathApprox, TNil { + private DataFlowType t; + + AccessPathApproxNil() { this = TNil(t) } + + override string toString() { result = concat(": " + ppReprType(t)) } + + override TypedContent getHead() { none() } + + override int len() { result = 0 } + + override DataFlowType getType() { result = t } + + override AccessPathFront getFront() { result = TFrontNil(t) } + + override AccessPathApprox pop(TypedContent head) { none() } +} + +abstract private class AccessPathApproxCons extends AccessPathApprox { } + +private class AccessPathApproxConsNil extends AccessPathApproxCons, TConsNil { + private TypedContent tc; + private DataFlowType t; + + AccessPathApproxConsNil() { this = TConsNil(tc, t) } + + override string toString() { + // The `concat` becomes "" if `ppReprType` has no result. + result = "[" + tc.toString() + "]" + concat(" : " + ppReprType(t)) + } + + override TypedContent getHead() { result = tc } + + override int len() { result = 1 } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { head = tc and result = TNil(t) } +} + +private class AccessPathApproxConsCons extends AccessPathApproxCons, TConsCons { + private TypedContent tc1; + private TypedContent tc2; + private int len; + + AccessPathApproxConsCons() { this = TConsCons(tc1, tc2, len) } + + override string toString() { + if len = 2 + then result = "[" + tc1.toString() + ", " + tc2.toString() + "]" + else result = "[" + tc1.toString() + ", " + tc2.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc1 } + + override int len() { result = len } + + override DataFlowType getType() { result = tc1.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc1) } + + override AccessPathApprox pop(TypedContent head) { + head = tc1 and + ( + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + } +} + +private class AccessPathApproxCons1 extends AccessPathApproxCons, TCons1 { + private TypedContent tc; + private int len; + + AccessPathApproxCons1() { this = TCons1(tc, len) } + + override string toString() { + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + } + + override TypedContent getHead() { result = tc } + + override int len() { result = len } + + override DataFlowType getType() { result = tc.getContainerType() } + + override AccessPathFront getFront() { result = TFrontHead(tc) } + + override AccessPathApprox pop(TypedContent head) { + head = tc and + ( + exists(TypedContent tc2 | Stage3::consCand(tc, TFrontHead(tc2), _) | + result = TConsCons(tc2, _, len - 1) + or + len = 2 and + result = TConsNil(tc2, _) + or + result = TCons1(tc2, len - 1) + ) + or + exists(DataFlowType t | + len = 1 and + Stage3::consCand(tc, TFrontNil(t), _) and + result = TNil(t) + ) + ) + } +} + +/** Gets the access path obtained by popping `tc` from `ap`, if any. */ +private AccessPathApprox pop(TypedContent tc, AccessPathApprox apa) { result = apa.pop(tc) } + +/** Gets the access path obtained by pushing `tc` onto `ap`. */ +private AccessPathApprox push(TypedContent tc, AccessPathApprox apa) { apa = pop(tc, result) } + +private newtype TAccessPathApproxOption = + TAccessPathApproxNone() or + TAccessPathApproxSome(AccessPathApprox apa) + +private class AccessPathApproxOption extends TAccessPathApproxOption { + string toString() { + this = TAccessPathApproxNone() and result = "" + or + this = TAccessPathApproxSome(any(AccessPathApprox apa | result = apa.toString())) + } +} + +private module Stage4Param implements MkStage::StageParam { + private module PrevStage = Stage3; + + class Ap = AccessPathApprox; + + class ApNil = AccessPathApproxNil; + + PrevStage::Ap getApprox(Ap ap) { result = ap.getFront() } + + ApNil getApNil(NodeEx node) { + PrevStage::revFlow(node, _) and result = TNil(node.getDataFlowType()) + } + + bindingset[tc, tail] + Ap apCons(TypedContent tc, Ap tail) { result = push(tc, tail) } + + pragma[noinline] + Content getHeadContent(Ap ap) { result = ap.getHead().getContent() } + + class ApOption = AccessPathApproxOption; + + ApOption apNone() { result = TAccessPathApproxNone() } + + ApOption apSome(Ap ap) { result = TAccessPathApproxSome(ap) } + + import Level1CallContext + import LocalCallContext + + predicate localStep( + NodeEx node1, FlowState state1, NodeEx node2, FlowState state2, boolean preservesValue, + ApNil ap, Configuration config, LocalCc lcc + ) { + localFlowBigStep(node1, state1, node2, state2, preservesValue, ap.getFront(), config, lcc) + } + + pragma[nomagic] + predicate flowOutOfCall( + DataFlowCall call, RetNodeEx node1, NodeEx node2, boolean allowsFieldFlow, Configuration config + ) { + exists(FlowState state | + flowOutOfCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + pragma[nomagic] + predicate flowIntoCall( + DataFlowCall call, ArgNodeEx node1, ParamNodeEx node2, boolean allowsFieldFlow, + Configuration config + ) { + exists(FlowState state | + flowIntoCallNodeCand2(call, node1, node2, allowsFieldFlow, config) and + PrevStage::revFlow(node2, pragma[only_bind_into](state), _, pragma[only_bind_into](config)) and + PrevStage::revFlowAlias(node1, pragma[only_bind_into](state), _, + pragma[only_bind_into](config)) + ) + } + + bindingset[node, state, ap, config] + predicate filter(NodeEx node, FlowState state, Ap ap, Configuration config) { any() } + + // Type checking is not necessary here as it has already been done in stage 3. + bindingset[ap, contentType] + predicate typecheckStore(Ap ap, DataFlowType contentType) { any() } +} + +private module Stage4 = MkStage::Stage; + +bindingset[conf, result] +private Configuration unbindConf(Configuration conf) { + exists(Configuration c | result = pragma[only_bind_into](c) and conf = pragma[only_bind_into](c)) +} + +pragma[nomagic] +private predicate nodeMayUseSummary0( + NodeEx n, DataFlowCallable c, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(AccessPathApprox apa0 | + Stage4::parameterMayFlowThrough(_, c, _, _) and + Stage4::revFlow(n, state, true, _, apa0, config) and + Stage4::fwdFlow(n, state, any(CallContextCall ccc), TAccessPathApproxSome(apa), apa0, config) and + n.getEnclosingCallable() = c + ) +} + +pragma[nomagic] +private predicate nodeMayUseSummary( + NodeEx n, FlowState state, AccessPathApprox apa, Configuration config +) { + exists(DataFlowCallable c | + Stage4::parameterMayFlowThrough(_, c, apa, config) and + nodeMayUseSummary0(n, c, state, apa, config) + ) +} + +private newtype TSummaryCtx = + TSummaryCtxNone() or + TSummaryCtxSome(ParamNodeEx p, FlowState state, AccessPath ap) { + exists(Configuration config | + Stage4::parameterMayFlowThrough(p, _, ap.getApprox(), config) and + Stage4::revFlow(p, state, _, config) + ) + } + +/** + * A context for generating flow summaries. This represents flow entry through + * a specific parameter with an access path of a specific shape. + * + * Summaries are only created for parameters that may flow through. + */ +abstract private class SummaryCtx extends TSummaryCtx { + abstract string toString(); +} + +/** A summary context from which no flow summary can be generated. */ +private class SummaryCtxNone extends SummaryCtx, TSummaryCtxNone { + override string toString() { result = "" } +} + +/** A summary context from which a flow summary can be generated. */ +private class SummaryCtxSome extends SummaryCtx, TSummaryCtxSome { + private ParamNodeEx p; + private FlowState s; + private AccessPath ap; + + SummaryCtxSome() { this = TSummaryCtxSome(p, s, ap) } + + ParameterPosition getParameterPos() { p.isParameterOf(_, result) } + + ParamNodeEx getParamNode() { result = p } + + override string toString() { result = p + ": " + ap } + + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + p.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** + * Gets the number of length 2 access path approximations that correspond to `apa`. + */ +private int count1to2unfold(AccessPathApproxCons1 apa, Configuration config) { + exists(TypedContent tc, int len | + tc = apa.getHead() and + len = apa.len() and + result = + strictcount(AccessPathFront apf | + Stage4::consCand(tc, any(AccessPathApprox ap | ap.getFront() = apf and ap.len() = len - 1), + config) + ) + ) +} + +private int countNodesUsingAccessPath(AccessPathApprox apa, Configuration config) { + result = + strictcount(NodeEx n, FlowState state | + Stage4::revFlow(n, state, apa, config) or nodeMayUseSummary(n, state, apa, config) + ) +} + +/** + * Holds if a length 2 access path approximation matching `apa` is expected + * to be expensive. + */ +private predicate expensiveLen1to2unfolding(AccessPathApproxCons1 apa, Configuration config) { + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = count1to2unfold(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + apLimit < aps and + tupleLimit < (aps - 1) * nodes + ) +} + +private AccessPathApprox getATail(AccessPathApprox apa, Configuration config) { + exists(TypedContent head | + apa.pop(head) = result and + Stage4::consCand(head, result, config) + ) +} + +/** + * Holds with `unfold = false` if a precise head-tail representation of `apa` is + * expected to be expensive. Holds with `unfold = true` otherwise. + */ +private predicate evalUnfold(AccessPathApprox apa, boolean unfold, Configuration config) { + if apa.getHead().forceHighPrecision() + then unfold = true + else + exists(int aps, int nodes, int apLimit, int tupleLimit | + aps = countPotentialAps(apa, config) and + nodes = countNodesUsingAccessPath(apa, config) and + accessPathCostLimits(apLimit, tupleLimit) and + if apLimit < aps and tupleLimit < (aps - 1) * nodes then unfold = false else unfold = true + ) +} + +/** + * Gets the number of `AccessPath`s that correspond to `apa`. + */ +private int countAps(AccessPathApprox apa, Configuration config) { + evalUnfold(apa, false, config) and + result = 1 and + (not apa instanceof AccessPathApproxCons1 or expensiveLen1to2unfolding(apa, config)) + or + evalUnfold(apa, false, config) and + result = count1to2unfold(apa, config) and + not expensiveLen1to2unfolding(apa, config) + or + evalUnfold(apa, true, config) and + result = countPotentialAps(apa, config) +} + +/** + * Gets the number of `AccessPath`s that would correspond to `apa` assuming + * that it is expanded to a precise head-tail representation. + */ +language[monotonicAggregates] +private int countPotentialAps(AccessPathApprox apa, Configuration config) { + apa instanceof AccessPathApproxNil and result = 1 + or + result = strictsum(AccessPathApprox tail | tail = getATail(apa, config) | countAps(tail, config)) +} + +private newtype TAccessPath = + TAccessPathNil(DataFlowType t) or + TAccessPathCons(TypedContent head, AccessPath tail) { + exists(AccessPathApproxCons apa | + not evalUnfold(apa, false, _) and + head = apa.getHead() and + tail.getApprox() = getATail(apa, _) + ) + } or + TAccessPathCons2(TypedContent head1, TypedContent head2, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + not expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head1 = apa.getHead() and + head2 = getATail(apa, _).getHead() + ) + } or + TAccessPathCons1(TypedContent head, int len) { + exists(AccessPathApproxCons apa | + evalUnfold(apa, false, _) and + expensiveLen1to2unfolding(apa, _) and + apa.len() = len and + head = apa.getHead() + ) + } + +private newtype TPathNode = + TPathNodeMid( + NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, Configuration config + ) { + // A PathNode is introduced by a source ... + Stage4::revFlow(node, state, config) and + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + or + // ... or a step from an existing PathNode to another node. + exists(PathNodeMid mid | + pathStep(mid, node, state, cc, sc, ap) and + pragma[only_bind_into](config) = mid.getConfiguration() and + Stage4::revFlow(node, state, ap.getApprox(), pragma[only_bind_into](config)) + ) + } or + TPathNodeSink(NodeEx node, FlowState state, Configuration config) { + exists(PathNodeMid sink | + sink.isAtSink() and + node = sink.getNodeEx() and + state = sink.getState() and + config = sink.getConfiguration() + ) + } + +/** + * A list of `TypedContent`s followed by a `DataFlowType`. If data flows from a + * source to a given node with a given `AccessPath`, this indicates the sequence + * of dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ +private class AccessPath extends TAccessPath { + /** Gets the head of this access path, if any. */ + abstract TypedContent getHead(); + + /** Gets the tail of this access path, if any. */ + abstract AccessPath getTail(); + + /** Gets the front of this access path. */ + abstract AccessPathFront getFront(); + + /** Gets the approximation of this access path. */ + abstract AccessPathApprox getApprox(); + + /** Gets the length of this access path. */ + abstract int length(); + + /** Gets a textual representation of this access path. */ + abstract string toString(); + + /** Gets the access path obtained by popping `tc` from this access path, if any. */ + final AccessPath pop(TypedContent tc) { + result = this.getTail() and + tc = this.getHead() + } + + /** Gets the access path obtained by pushing `tc` onto this access path. */ + final AccessPath push(TypedContent tc) { this = result.pop(tc) } +} + +private class AccessPathNil extends AccessPath, TAccessPathNil { + private DataFlowType t; + + AccessPathNil() { this = TAccessPathNil(t) } + + DataFlowType getType() { result = t } + + override TypedContent getHead() { none() } + + override AccessPath getTail() { none() } + + override AccessPathFrontNil getFront() { result = TFrontNil(t) } + + override AccessPathApproxNil getApprox() { result = TNil(t) } + + override int length() { result = 0 } + + override string toString() { result = concat(": " + ppReprType(t)) } +} + +private class AccessPathCons extends AccessPath, TAccessPathCons { + private TypedContent head; + private AccessPath tail; + + AccessPathCons() { this = TAccessPathCons(head, tail) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { result = tail } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { + result = TConsNil(head, tail.(AccessPathNil).getType()) + or + result = TConsCons(head, tail.getHead(), this.length()) + or + result = TCons1(head, this.length()) + } + + override int length() { result = 1 + tail.length() } + + private string toStringImpl(boolean needsSuffix) { + exists(DataFlowType t | + tail = TAccessPathNil(t) and + needsSuffix = false and + result = head.toString() + "]" + concat(" : " + ppReprType(t)) + ) + or + result = head + ", " + tail.(AccessPathCons).toStringImpl(needsSuffix) + or + exists(TypedContent tc2, TypedContent tc3, int len | tail = TAccessPathCons2(tc2, tc3, len) | + result = head + ", " + tc2 + ", " + tc3 + ", ... (" and len > 2 and needsSuffix = true + or + result = head + ", " + tc2 + ", " + tc3 + "]" and len = 2 and needsSuffix = false + ) + or + exists(TypedContent tc2, int len | tail = TAccessPathCons1(tc2, len) | + result = head + ", " + tc2 + ", ... (" and len > 1 and needsSuffix = true + or + result = head + ", " + tc2 + "]" and len = 1 and needsSuffix = false + ) + } + + override string toString() { + result = "[" + this.toStringImpl(true) + this.length().toString() + ")]" + or + result = "[" + this.toStringImpl(false) + } +} + +private class AccessPathCons2 extends AccessPath, TAccessPathCons2 { + private TypedContent head1; + private TypedContent head2; + private int len; + + AccessPathCons2() { this = TAccessPathCons2(head1, head2, len) } + + override TypedContent getHead() { result = head1 } + + override AccessPath getTail() { + Stage4::consCand(head1, result.getApprox(), _) and + result.getHead() = head2 and + result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head1) } + + override AccessPathApproxCons getApprox() { + result = TConsCons(head1, head2, len) or + result = TCons1(head1, len) + } + + override int length() { result = len } + + override string toString() { + if len = 2 + then result = "[" + head1.toString() + ", " + head2.toString() + "]" + else + result = "[" + head1.toString() + ", " + head2.toString() + ", ... (" + len.toString() + ")]" + } +} + +private class AccessPathCons1 extends AccessPath, TAccessPathCons1 { + private TypedContent head; + private int len; + + AccessPathCons1() { this = TAccessPathCons1(head, len) } + + override TypedContent getHead() { result = head } + + override AccessPath getTail() { + Stage4::consCand(head, result.getApprox(), _) and result.length() = len - 1 + } + + override AccessPathFrontHead getFront() { result = TFrontHead(head) } + + override AccessPathApproxCons getApprox() { result = TCons1(head, len) } + + override int length() { result = len } + + override string toString() { + if len = 1 + then result = "[" + head.toString() + "]" + else result = "[" + head.toString() + ", ... (" + len.toString() + ")]" + } +} + +/** + * A `Node` augmented with a call context (except for sinks), an access path, and a configuration. + * Only those `PathNode`s that are reachable from a source are generated. + */ +class PathNode extends TPathNode { + /** Gets a textual representation of this element. */ + string toString() { none() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { none() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + none() + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.(PathNodeImpl).getNodeEx().projectToNode() = result } + + /** Gets the `FlowState` of this node. */ + FlowState getState() { none() } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + final PathNode getASuccessor() { + result = this.(PathNodeImpl).getANonHiddenSuccessor() and + reach(this) and + reach(result) + } + + /** Holds if this node is a source. */ + predicate isSource() { none() } +} + +abstract private class PathNodeImpl extends PathNode { + abstract PathNodeImpl getASuccessorImpl(); + + private PathNodeImpl getASuccessorIfHidden() { + this.isHidden() and + result = this.getASuccessorImpl() + } + + final PathNodeImpl getANonHiddenSuccessor() { + result = this.getASuccessorImpl().getASuccessorIfHidden*() and + not this.isHidden() and + not result.isHidden() + } + + abstract NodeEx getNodeEx(); + + predicate isHidden() { + not this.getConfiguration().includeHiddenNodes() and + ( + hiddenNode(this.getNodeEx().asNode()) and + not this.isSource() and + not this instanceof PathNodeSink + or + this.getNodeEx() instanceof TNodeImplicitRead + ) + } + + private string ppAp() { + this instanceof PathNodeSink and result = "" + or + exists(string s | s = this.(PathNodeMid).getAp().toString() | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + this instanceof PathNodeSink and result = "" + or + result = " <" + this.(PathNodeMid).getCallContext().toString() + ">" + } + + override string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + override string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + override predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } +} + +/** Holds if `n` can reach a sink. */ +private predicate directReach(PathNodeImpl n) { + n instanceof PathNodeSink or directReach(n.getANonHiddenSuccessor()) +} + +/** Holds if `n` can reach a sink or is used in a subpath that can reach a sink. */ +private predicate reach(PathNode n) { directReach(n) or Subpaths::retReach(n) } + +/** Holds if `n1.getASuccessor() = n2` and `n2` can reach a sink. */ +private predicate pathSucc(PathNodeImpl n1, PathNode n2) { + n1.getANonHiddenSuccessor() = n2 and directReach(n2) +} + +private predicate pathSuccPlus(PathNode n1, PathNode n2) = fastTC(pathSucc/2)(n1, n2) + +/** + * Provides the query predicates needed to include a graph in a path-problem query. + */ +module PathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PathNode a, PathNode b) { a.getASuccessor() = b } + + /** Holds if `n` is a node in the graph of data flow path explanations. */ + query predicate nodes(PathNode n, string key, string val) { + reach(n) and key = "semmle.label" and val = n.toString() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + query predicate subpaths(PathNode arg, PathNode par, PathNode ret, PathNode out) { + Subpaths::subpaths(arg, par, ret, out) and + reach(arg) and + reach(par) and + reach(ret) and + reach(out) + } +} + +/** + * An intermediate flow graph node. This is a triple consisting of a `Node`, + * a `CallContext`, and a `Configuration`. + */ +private class PathNodeMid extends PathNodeImpl, TPathNodeMid { + NodeEx node; + FlowState state; + CallContext cc; + SummaryCtx sc; + AccessPath ap; + Configuration config; + + PathNodeMid() { this = TPathNodeMid(node, state, cc, sc, ap, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + SummaryCtx getSummaryCtx() { result = sc } + + AccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + private PathNodeMid getSuccMid() { + pathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx(), result.getAp()) and + result.getConfiguration() = unbindConf(this.getConfiguration()) + } + + override PathNodeImpl getASuccessorImpl() { + // an intermediate step to another intermediate node + result = this.getSuccMid() + or + // a final step to a sink + result = this.getSuccMid().projectToSink() + } + + override predicate isSource() { + sourceNode(node, state, config) and + ( + if hasSourceCallCtx(config) + then cc instanceof CallContextSomeCall + else cc instanceof CallContextAny + ) and + sc instanceof SummaryCtxNone and + ap = TAccessPathNil(node.getDataFlowType()) + } + + predicate isAtSink() { + sinkNode(node, state, config) and + ap instanceof AccessPathNil and + if hasSinkCallCtx(config) + then + // For `FeatureHasSinkCallContext` the condition `cc instanceof CallContextNoCall` + // is exactly what we need to check. This also implies + // `sc instanceof SummaryCtxNone`. + // For `FeatureEqualSourceSinkCallContext` the initial call context was + // set to `CallContextSomeCall` and jumps are disallowed, so + // `cc instanceof CallContextNoCall` never holds. On the other hand, + // in this case there's never any need to enter a call except to identify + // a summary, so the condition in `pathIntoCallable` enforces this, which + // means that `sc instanceof SummaryCtxNone` holds if and only if we are + // in the call context of the source. + sc instanceof SummaryCtxNone or + cc instanceof CallContextNoCall + else any() + } + + PathNodeSink projectToSink() { + this.isAtSink() and + result.getNodeEx() = node and + result.getState() = state and + result.getConfiguration() = unbindConf(config) + } +} + +/** + * A flow graph node corresponding to a sink. This is disjoint from the + * intermediate nodes in order to uniquely correspond to a given sink by + * excluding the `CallContext`. + */ +private class PathNodeSink extends PathNodeImpl, TPathNodeSink { + NodeEx node; + FlowState state; + Configuration config; + + PathNodeSink() { this = TPathNodeSink(node, state, config) } + + override NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + override Configuration getConfiguration() { result = config } + + override PathNodeImpl getASuccessorImpl() { none() } + + override predicate isSource() { sourceNode(node, state, config) } +} + +private predicate pathNode( + PathNodeMid mid, NodeEx midnode, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap, + Configuration conf, LocalCallContext localCC +) { + midnode = mid.getNodeEx() and + state = mid.getState() and + conf = mid.getConfiguration() and + cc = mid.getCallContext() and + sc = mid.getSummaryCtx() and + localCC = + getLocalCallContext(pragma[only_bind_into](pragma[only_bind_out](cc)), + midnode.getEnclosingCallable()) and + ap = mid.getAp() +} + +/** + * Holds if data may flow from `mid` to `node`. The last step in or out of + * a callable is recorded by `cc`. + */ +pragma[nomagic] +private predicate pathStep( + PathNodeMid mid, NodeEx node, FlowState state, CallContext cc, SummaryCtx sc, AccessPath ap +) { + exists(NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC | + pathNode(mid, midnode, state0, cc, sc, ap, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, true, _, conf, localCC) + ) + or + exists( + AccessPath ap0, NodeEx midnode, FlowState state0, Configuration conf, LocalCallContext localCC + | + pathNode(mid, midnode, state0, cc, sc, ap0, conf, localCC) and + localFlowBigStep(midnode, state0, node, state, false, ap.getFront(), conf, localCC) and + ap0 instanceof AccessPathNil + ) + or + jumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + ap = mid.getAp() + or + additionalJumpStep(mid.getNodeEx(), node, mid.getConfiguration()) and + state = mid.getState() and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, mid.getConfiguration()) and + cc instanceof CallContextAny and + sc instanceof SummaryCtxNone and + mid.getAp() instanceof AccessPathNil and + ap = TAccessPathNil(node.getDataFlowType()) + or + exists(TypedContent tc | pathStoreStep(mid, node, state, ap.pop(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + exists(TypedContent tc | pathReadStep(mid, node, state, ap.push(tc), tc, cc)) and + sc = mid.getSummaryCtx() + or + pathIntoCallable(mid, node, state, _, cc, sc, _, _) and ap = mid.getAp() + or + pathOutOfCallable(mid, node, state, cc) and ap = mid.getAp() and sc instanceof SummaryCtxNone + or + pathThroughCallable(mid, node, state, cc, ap) and sc = mid.getSummaryCtx() +} + +pragma[nomagic] +private predicate pathReadStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + tc = ap0.getHead() and + Stage4::readStepCand(mid.getNodeEx(), tc.getContent(), node, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +pragma[nomagic] +private predicate pathStoreStep( + PathNodeMid mid, NodeEx node, FlowState state, AccessPath ap0, TypedContent tc, CallContext cc +) { + ap0 = mid.getAp() and + Stage4::storeStepCand(mid.getNodeEx(), _, tc, node, _, mid.getConfiguration()) and + state = mid.getState() and + cc = mid.getCallContext() +} + +private predicate pathOutOfCallable0( + PathNodeMid mid, ReturnPosition pos, FlowState state, CallContext innercc, AccessPathApprox apa, + Configuration config +) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + apa = mid.getAp().getApprox() and + config = mid.getConfiguration() +} + +pragma[nomagic] +private predicate pathOutOfCallable1( + PathNodeMid mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPathApprox apa, Configuration config +) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + pathOutOfCallable0(mid, pos, state, innercc, apa, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) +} + +pragma[noinline] +private NodeEx getAnOutNodeFlow( + ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config +) { + result.asNode() = kind.getAnOutNode(call) and + Stage4::revFlow(result, _, apa, config) +} + +/** + * Holds if data may flow from `mid` to `out`. The last step of this path + * is a return from a callable and is recorded by `cc`, if needed. + */ +pragma[noinline] +private predicate pathOutOfCallable(PathNodeMid mid, NodeEx out, FlowState state, CallContext cc) { + exists(ReturnKindExt kind, DataFlowCall call, AccessPathApprox apa, Configuration config | + pathOutOfCallable1(mid, call, kind, state, cc, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` to the `i`th argument of `call` in `cc`. + */ +pragma[noinline] +private predicate pathIntoArg( + PathNodeMid mid, ParameterPosition ppos, FlowState state, CallContext cc, DataFlowCall call, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(ArgNodeEx arg, ArgumentPosition apos | + pathNode(mid, arg, state, cc, _, ap, config, _) and + arg.asNode().(ArgNode).argumentOf(call, apos) and + apa = ap.getApprox() and + parameterMatch(ppos, apos) + ) +} + +pragma[nomagic] +private predicate parameterCand( + DataFlowCallable callable, ParameterPosition pos, AccessPathApprox apa, Configuration config +) { + exists(ParamNodeEx p | + Stage4::revFlow(p, _, apa, config) and + p.isParameterOf(callable, pos) + ) +} + +pragma[nomagic] +private predicate pathIntoCallable0( + PathNodeMid mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, AccessPath ap, Configuration config +) { + exists(AccessPathApprox apa | + pathIntoArg(mid, pragma[only_bind_into](pos), state, outercc, call, ap, + pragma[only_bind_into](apa), pragma[only_bind_into](config)) and + callable = resolveCall(call, outercc) and + parameterCand(callable, pragma[only_bind_into](pos), pragma[only_bind_into](apa), + pragma[only_bind_into](config)) + ) +} + +/** + * Holds if data may flow from `mid` to `p` through `call`. The contexts + * before and after entering the callable are `outercc` and `innercc`, + * respectively. + */ +pragma[nomagic] +private predicate pathIntoCallable( + PathNodeMid mid, ParamNodeEx p, FlowState state, CallContext outercc, CallContextCall innercc, + SummaryCtx sc, DataFlowCall call, Configuration config +) { + exists(ParameterPosition pos, DataFlowCallable callable, AccessPath ap | + pathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + ( + sc = TSummaryCtxSome(p, state, ap) + or + not exists(TSummaryCtxSome(p, state, ap)) and + sc = TSummaryCtxNone() and + // When the call contexts of source and sink needs to match then there's + // never any reason to enter a callable except to find a summary. See also + // the comment in `PathNodeMid::isAtSink`. + not config.getAFeature() instanceof FeatureEqualSourceSinkCallContext + ) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) +} + +/** Holds if data may flow from a parameter given by `sc` to a return of kind `kind`. */ +pragma[nomagic] +private predicate paramFlowsThrough( + ReturnKindExt kind, FlowState state, CallContextCall cc, SummaryCtxSome sc, AccessPath ap, + AccessPathApprox apa, Configuration config +) { + exists(PathNodeMid mid, RetNodeEx ret, ParameterPosition pos | + pathNode(mid, ret, state, cc, sc, ap, config, _) and + kind = ret.getKind() and + apa = ap.getApprox() and + pos = sc.getParameterPos() and + // we don't expect a parameter to return stored in itself, unless explicitly allowed + ( + not kind.(ParamUpdateReturnKind).getPosition() = pos + or + sc.getParamNode().allowParameterReturnInSelf() + ) + ) +} + +pragma[nomagic] +private predicate pathThroughCallable0( + DataFlowCall call, PathNodeMid mid, ReturnKindExt kind, FlowState state, CallContext cc, + AccessPath ap, AccessPathApprox apa, Configuration config +) { + exists(CallContext innercc, SummaryCtx sc | + pathIntoCallable(mid, _, _, cc, innercc, sc, call, config) and + paramFlowsThrough(kind, state, innercc, sc, ap, apa, config) + ) +} + +/** + * Holds if data may flow from `mid` through a callable to the node `out`. + * The context `cc` is restored to its value prior to entering the callable. + */ +pragma[noinline] +private predicate pathThroughCallable( + PathNodeMid mid, NodeEx out, FlowState state, CallContext cc, AccessPath ap +) { + exists(DataFlowCall call, ReturnKindExt kind, AccessPathApprox apa, Configuration config | + pathThroughCallable0(call, mid, kind, state, cc, ap, apa, config) and + out = getAnOutNodeFlow(kind, call, apa, config) + ) +} + +private module Subpaths { + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths01( + PathNodeImpl arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + exists(Configuration config | + pathThroughCallable(arg, out, pragma[only_bind_into](sout), _, pragma[only_bind_into](apout)) and + pathIntoCallable(arg, par, _, _, innercc, sc, _, config) and + paramFlowsThrough(kind, pragma[only_bind_into](sout), innercc, sc, + pragma[only_bind_into](apout), _, unbindConf(config)) and + not arg.isHidden() + ) + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple and `ret` is determined by + * `kind`, `sc`, `sout`, `apout`, and `innercc`. + */ + pragma[nomagic] + private predicate subpaths02( + PathNode arg, ParamNodeEx par, SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, + NodeEx out, FlowState sout, AccessPath apout + ) { + subpaths01(arg, par, sc, innercc, kind, out, sout, apout) and + out.asNode() = kind.getAnOutNode(_) + } + + pragma[nomagic] + private Configuration getPathNodeConf(PathNode n) { result = n.getConfiguration() } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple. + */ + pragma[nomagic] + private predicate subpaths03( + PathNode arg, ParamNodeEx par, PathNodeMid ret, NodeEx out, FlowState sout, AccessPath apout + ) { + exists(SummaryCtxSome sc, CallContext innercc, ReturnKindExt kind, RetNodeEx retnode | + subpaths02(arg, par, sc, innercc, kind, out, sout, apout) and + pathNode(ret, retnode, sout, innercc, sc, apout, unbindConf(getPathNodeConf(arg)), _) and + kind = retnode.getKind() + ) + } + + private PathNodeImpl localStepToHidden(PathNodeImpl n) { + n.getASuccessorImpl() = result and + result.isHidden() and + exists(NodeEx n1, NodeEx n2 | n1 = n.getNodeEx() and n2 = result.getNodeEx() | + localFlowBigStep(n1, _, n2, _, _, _, _, _) or + store(n1, _, n2, _, _) or + readSet(n1, _, n2, _) + ) + } + + pragma[nomagic] + private predicate hasSuccessor(PathNodeImpl pred, PathNodeMid succ, NodeEx succNode) { + succ = pred.getANonHiddenSuccessor() and + succNode = succ.getNodeEx() + } + + /** + * Holds if `(arg, par, ret, out)` forms a subpath-tuple, that is, flow through + * a subpath between `par` and `ret` with the connecting edges `arg -> par` and + * `ret -> out` is summarized as the edge `arg -> out`. + */ + predicate subpaths(PathNodeImpl arg, PathNodeImpl par, PathNodeImpl ret, PathNode out) { + exists(ParamNodeEx p, NodeEx o, FlowState sout, AccessPath apout, PathNodeMid out0 | + pragma[only_bind_into](arg).getANonHiddenSuccessor() = pragma[only_bind_into](out0) and + subpaths03(pragma[only_bind_into](arg), p, localStepToHidden*(ret), o, sout, apout) and + hasSuccessor(pragma[only_bind_into](arg), par, p) and + not ret.isHidden() and + pathNode(out0, o, sout, _, _, apout, _, _) + | + out = out0 or out = out0.projectToSink() + ) + } + + /** + * Holds if `n` can reach a return node in a summarized subpath that can reach a sink. + */ + predicate retReach(PathNodeImpl n) { + exists(PathNode out | subpaths(_, _, n, out) | directReach(out) or retReach(out)) + or + exists(PathNodeImpl mid | + retReach(mid) and + n.getANonHiddenSuccessor() = mid and + not subpaths(_, mid, _, _) + ) + } +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +private predicate flowsTo( + PathNode flowsource, PathNodeSink flowsink, Node source, Node sink, Configuration configuration +) { + flowsource.isSource() and + flowsource.getConfiguration() = configuration and + flowsource.(PathNodeImpl).getNodeEx().asNode() = source and + (flowsource = flowsink or pathSuccPlus(flowsource, flowsink)) and + flowsink.getNodeEx().asNode() = sink +} + +/** + * Holds if data can flow (inter-procedurally) from `source` to `sink`. + * + * Will only have results if `configuration` has non-empty sources and + * sinks. + */ +predicate flowsTo(Node source, Node sink, Configuration configuration) { + flowsTo(_, _, source, sink, configuration) +} + +private predicate finalStats( + boolean fwd, int nodes, int fields, int conscand, int states, int tuples +) { + fwd = true and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0)) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0)) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap)) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state)) and + tuples = count(PathNode pn) + or + fwd = false and + nodes = count(NodeEx n0 | exists(PathNodeImpl pn | pn.getNodeEx() = n0 and reach(pn))) and + fields = count(TypedContent f0 | exists(PathNodeMid pn | pn.getAp().getHead() = f0 and reach(pn))) and + conscand = count(AccessPath ap | exists(PathNodeMid pn | pn.getAp() = ap and reach(pn))) and + states = count(FlowState state | exists(PathNodeMid pn | pn.getState() = state and reach(pn))) and + tuples = count(PathNode pn | reach(pn)) +} + +/** + * INTERNAL: Only for debugging. + * + * Calculates per-stage metrics for data flow. + */ +predicate stageStats( + int n, string stage, int nodes, int fields, int conscand, int states, int tuples, + Configuration config +) { + stage = "1 Fwd" and + n = 10 and + Stage1::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "1 Rev" and + n = 15 and + Stage1::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Fwd" and + n = 20 and + Stage2::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "2 Rev" and + n = 25 and + Stage2::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Fwd" and + n = 30 and + Stage3::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "3 Rev" and + n = 35 and + Stage3::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Fwd" and + n = 40 and + Stage4::stats(true, nodes, fields, conscand, states, tuples, config) + or + stage = "4 Rev" and + n = 45 and + Stage4::stats(false, nodes, fields, conscand, states, tuples, config) + or + stage = "5 Fwd" and n = 50 and finalStats(true, nodes, fields, conscand, states, tuples) + or + stage = "5 Rev" and n = 55 and finalStats(false, nodes, fields, conscand, states, tuples) +} + +private module FlowExploration { + private predicate callableStep(DataFlowCallable c1, DataFlowCallable c2, Configuration config) { + exists(NodeEx node1, NodeEx node2 | + jumpStep(node1, node2, config) + or + additionalJumpStep(node1, node2, config) + or + additionalJumpStateStep(node1, _, node2, _, config) + or + // flow into callable + viableParamArgEx(_, node2, node1) + or + // flow out of a callable + viableReturnPosOutEx(_, node1.(RetNodeEx).getReturnPosition(), node2) + | + c1 = node1.getEnclosingCallable() and + c2 = node2.getEnclosingCallable() and + c1 != c2 + ) + } + + private predicate interestingCallableSrc(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSource(n) or config.isSource(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSrc(mid, config) and callableStep(mid, c, config) + ) + } + + private predicate interestingCallableSink(DataFlowCallable c, Configuration config) { + exists(Node n | config.isSink(n) or config.isSink(n, _) | c = getNodeEnclosingCallable(n)) + or + exists(DataFlowCallable mid | + interestingCallableSink(mid, config) and callableStep(c, mid, config) + ) + } + + private newtype TCallableExt = + TCallable(DataFlowCallable c, Configuration config) { + interestingCallableSrc(c, config) or + interestingCallableSink(c, config) + } or + TCallableSrc() or + TCallableSink() + + private predicate callableExtSrc(TCallableSrc src) { any() } + + private predicate callableExtSink(TCallableSink sink) { any() } + + private predicate callableExtStepFwd(TCallableExt ce1, TCallableExt ce2) { + exists(DataFlowCallable c1, DataFlowCallable c2, Configuration config | + callableStep(c1, c2, config) and + ce1 = TCallable(c1, pragma[only_bind_into](config)) and + ce2 = TCallable(c2, pragma[only_bind_into](config)) + ) + or + exists(Node n, Configuration config | + ce1 = TCallableSrc() and + (config.isSource(n) or config.isSource(n, _)) and + ce2 = TCallable(getNodeEnclosingCallable(n), config) + ) + or + exists(Node n, Configuration config | + ce2 = TCallableSink() and + (config.isSink(n) or config.isSink(n, _)) and + ce1 = TCallable(getNodeEnclosingCallable(n), config) + ) + } + + private predicate callableExtStepRev(TCallableExt ce1, TCallableExt ce2) { + callableExtStepFwd(ce2, ce1) + } + + private int distSrcExt(TCallableExt c) = + shortestDistances(callableExtSrc/1, callableExtStepFwd/2)(_, c, result) + + private int distSinkExt(TCallableExt c) = + shortestDistances(callableExtSink/1, callableExtStepRev/2)(_, c, result) + + private int distSrc(DataFlowCallable c, Configuration config) { + result = distSrcExt(TCallable(c, config)) - 1 + } + + private int distSink(DataFlowCallable c, Configuration config) { + result = distSinkExt(TCallable(c, config)) - 1 + } + + private newtype TPartialAccessPath = + TPartialNil(DataFlowType t) or + TPartialCons(TypedContent tc, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `TypedContent`s followed by a `Type`, but only the first + * element of the list and its length are tracked. If data flows from a source to + * a given node with a given `AccessPath`, this indicates the sequence of + * dereference operations needed to get from the value in the node to the + * tracked object. The final type indicates the type of the tracked object. + */ + private class PartialAccessPath extends TPartialAccessPath { + abstract string toString(); + + TypedContent getHead() { this = TPartialCons(result, _) } + + int len() { + this = TPartialNil(_) and result = 0 + or + this = TPartialCons(_, result) + } + + DataFlowType getType() { + this = TPartialNil(result) + or + exists(TypedContent head | this = TPartialCons(head, _) | result = head.getContainerType()) + } + } + + private class PartialAccessPathNil extends PartialAccessPath, TPartialNil { + override string toString() { + exists(DataFlowType t | this = TPartialNil(t) | result = concat(": " + ppReprType(t))) + } + } + + private class PartialAccessPathCons extends PartialAccessPath, TPartialCons { + override string toString() { + exists(TypedContent tc, int len | this = TPartialCons(tc, len) | + if len = 1 + then result = "[" + tc.toString() + "]" + else result = "[" + tc.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private newtype TRevPartialAccessPath = + TRevPartialNil() or + TRevPartialCons(Content c, int len) { len in [1 .. accessPathLimit()] } + + /** + * Conceptually a list of `Content`s, but only the first + * element of the list and its length are tracked. + */ + private class RevPartialAccessPath extends TRevPartialAccessPath { + abstract string toString(); + + Content getHead() { this = TRevPartialCons(result, _) } + + int len() { + this = TRevPartialNil() and result = 0 + or + this = TRevPartialCons(_, result) + } + } + + private class RevPartialAccessPathNil extends RevPartialAccessPath, TRevPartialNil { + override string toString() { result = "" } + } + + private class RevPartialAccessPathCons extends RevPartialAccessPath, TRevPartialCons { + override string toString() { + exists(Content c, int len | this = TRevPartialCons(c, len) | + if len = 1 + then result = "[" + c.toString() + "]" + else result = "[" + c.toString() + ", ... (" + len.toString() + ")]" + ) + } + } + + private predicate relevantState(FlowState state) { + sourceNode(_, state, _) or + sinkNode(_, state, _) or + additionalLocalStateStep(_, state, _, _, _) or + additionalLocalStateStep(_, _, _, state, _) or + additionalJumpStateStep(_, state, _, _, _) or + additionalJumpStateStep(_, _, _, state, _) + } + + private newtype TSummaryCtx1 = + TSummaryCtx1None() or + TSummaryCtx1Param(ParamNodeEx p) + + private newtype TSummaryCtx2 = + TSummaryCtx2None() or + TSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TSummaryCtx3 = + TSummaryCtx3None() or + TSummaryCtx3Some(PartialAccessPath ap) + + private newtype TRevSummaryCtx1 = + TRevSummaryCtx1None() or + TRevSummaryCtx1Some(ReturnPosition pos) + + private newtype TRevSummaryCtx2 = + TRevSummaryCtx2None() or + TRevSummaryCtx2Some(FlowState s) { relevantState(s) } + + private newtype TRevSummaryCtx3 = + TRevSummaryCtx3None() or + TRevSummaryCtx3Some(RevPartialAccessPath ap) + + private newtype TPartialPathNode = + TPartialPathNodeFwd( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = TPartialNil(node.getDataFlowType()) and + exists(config.explorationLimit()) + or + partialPathNodeMk0(node, state, cc, sc1, sc2, sc3, ap, config) and + distSrc(node.getEnclosingCallable(), config) <= config.explorationLimit() + } or + TPartialPathNodeRev( + NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, TRevSummaryCtx3 sc3, + RevPartialAccessPath ap, Configuration config + ) { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() and + exists(config.explorationLimit()) + or + exists(PartialPathNodeRev mid | + revPartialPathStep(mid, node, state, sc1, sc2, sc3, ap, config) and + not clearsContentEx(node, ap.getHead()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead()) + ) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + distSink(node.getEnclosingCallable(), config) <= config.explorationLimit() + ) + } + + pragma[nomagic] + private predicate partialPathNodeMk0( + NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStep(mid, node, state, cc, sc1, sc2, sc3, ap, config) and + not fullBarrier(node, config) and + not stateBarrier(node, state, config) and + not clearsContentEx(node, ap.getHead().getContent()) and + ( + notExpectsContent(node) or + expectsContentEx(node, ap.getHead().getContent()) + ) and + if node.asNode() instanceof CastingNode + then compatibleTypes(node.getDataFlowType(), ap.getType()) + else any() + ) + } + + /** + * A `Node` augmented with a call context, an access path, and a configuration. + */ + class PartialPathNode extends TPartialPathNode { + /** Gets a textual representation of this element. */ + string toString() { result = this.getNodeEx().toString() + this.ppAp() } + + /** + * Gets a textual representation of this element, including a textual + * representation of the call context. + */ + string toStringWithContext() { + result = this.getNodeEx().toString() + this.ppAp() + this.ppCtx() + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getNodeEx().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets the underlying `Node`. */ + final Node getNode() { this.getNodeEx().projectToNode() = result } + + FlowState getState() { none() } + + private NodeEx getNodeEx() { + result = this.(PartialPathNodeFwd).getNodeEx() or + result = this.(PartialPathNodeRev).getNodeEx() + } + + /** Gets the associated configuration. */ + Configuration getConfiguration() { none() } + + /** Gets a successor of this node, if any. */ + PartialPathNode getASuccessor() { none() } + + /** + * Gets the approximate distance to the nearest source measured in number + * of interprocedural steps. + */ + int getSourceDistance() { + result = distSrc(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + /** + * Gets the approximate distance to the nearest sink measured in number + * of interprocedural steps. + */ + int getSinkDistance() { + result = distSink(this.getNodeEx().getEnclosingCallable(), this.getConfiguration()) + } + + private string ppAp() { + exists(string s | + s = this.(PartialPathNodeFwd).getAp().toString() or + s = this.(PartialPathNodeRev).getAp().toString() + | + if s = "" then result = "" else result = " " + s + ) + } + + private string ppCtx() { + result = " <" + this.(PartialPathNodeFwd).getCallContext().toString() + ">" + } + + /** Holds if this is a source in a forward-flow path. */ + predicate isFwdSource() { this.(PartialPathNodeFwd).isSource() } + + /** Holds if this is a sink in a reverse-flow path. */ + predicate isRevSink() { this.(PartialPathNodeRev).isSink() } + } + + /** + * Provides the query predicates needed to include a graph in a path-problem query. + */ + module PartialPathGraph { + /** Holds if `(a,b)` is an edge in the graph of data flow path explanations. */ + query predicate edges(PartialPathNode a, PartialPathNode b) { a.getASuccessor() = b } + } + + private class PartialPathNodeFwd extends PartialPathNode, TPartialPathNodeFwd { + NodeEx node; + FlowState state; + CallContext cc; + TSummaryCtx1 sc1; + TSummaryCtx2 sc2; + TSummaryCtx3 sc3; + PartialAccessPath ap; + Configuration config; + + PartialPathNodeFwd() { this = TPartialPathNodeFwd(node, state, cc, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + CallContext getCallContext() { result = cc } + + TSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TSummaryCtx3 getSummaryCtx3() { result = sc3 } + + PartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeFwd getASuccessor() { + partialPathStep(this, result.getNodeEx(), result.getState(), result.getCallContext(), + result.getSummaryCtx1(), result.getSummaryCtx2(), result.getSummaryCtx3(), result.getAp(), + result.getConfiguration()) + } + + predicate isSource() { + sourceNode(node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap instanceof TPartialNil + } + } + + private class PartialPathNodeRev extends PartialPathNode, TPartialPathNodeRev { + NodeEx node; + FlowState state; + TRevSummaryCtx1 sc1; + TRevSummaryCtx2 sc2; + TRevSummaryCtx3 sc3; + RevPartialAccessPath ap; + Configuration config; + + PartialPathNodeRev() { this = TPartialPathNodeRev(node, state, sc1, sc2, sc3, ap, config) } + + NodeEx getNodeEx() { result = node } + + override FlowState getState() { result = state } + + TRevSummaryCtx1 getSummaryCtx1() { result = sc1 } + + TRevSummaryCtx2 getSummaryCtx2() { result = sc2 } + + TRevSummaryCtx3 getSummaryCtx3() { result = sc3 } + + RevPartialAccessPath getAp() { result = ap } + + override Configuration getConfiguration() { result = config } + + override PartialPathNodeRev getASuccessor() { + revPartialPathStep(result, this.getNodeEx(), this.getState(), this.getSummaryCtx1(), + this.getSummaryCtx2(), this.getSummaryCtx3(), this.getAp(), this.getConfiguration()) + } + + predicate isSink() { + sinkNode(node, state, config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = TRevPartialNil() + } + } + + private predicate partialPathStep( + PartialPathNodeFwd mid, NodeEx node, FlowState state, CallContext cc, TSummaryCtx1 sc1, + TSummaryCtx2 sc2, TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + not isUnreachableInCallCached(node.asNode(), cc.(CallContextSpecificCall).getCall()) and + ( + localFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalLocalStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + ) + or + jumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(mid.getNodeEx(), node, config) and + state = mid.getState() and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + additionalJumpStateStep(mid.getNodeEx(), mid.getState(), node, state, config) and + cc instanceof CallContextAny and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() and + mid.getAp() instanceof PartialAccessPathNil and + ap = TPartialNil(node.getDataFlowType()) and + config = mid.getConfiguration() + or + partialPathStoreStep(mid, _, _, node, ap) and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(PartialAccessPath ap0, TypedContent tc | + partialPathReadStep(mid, ap0, tc, node, cc, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsFwd(ap, tc, ap0, config) + ) + or + partialPathIntoCallable(mid, node, state, _, cc, sc1, sc2, sc3, _, ap, config) + or + partialPathOutOfCallable(mid, node, state, cc, ap, config) and + sc1 = TSummaryCtx1None() and + sc2 = TSummaryCtx2None() and + sc3 = TSummaryCtx3None() + or + partialPathThroughCallable(mid, node, state, cc, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + bindingset[result, i] + private int unbindInt(int i) { pragma[only_bind_out](i) = pragma[only_bind_out](result) } + + pragma[inline] + private predicate partialPathStoreStep( + PartialPathNodeFwd mid, PartialAccessPath ap1, TypedContent tc, NodeEx node, + PartialAccessPath ap2 + ) { + exists(NodeEx midNode, DataFlowType contentType | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + store(midNode, tc, node, contentType, mid.getConfiguration()) and + ap2.getHead() = tc and + ap2.len() = unbindInt(ap1.len() + 1) and + compatibleTypes(ap1.getType(), contentType) + ) + } + + pragma[nomagic] + private predicate apConsFwd( + PartialAccessPath ap1, TypedContent tc, PartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeFwd mid | + partialPathStoreStep(mid, ap1, tc, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate partialPathReadStep( + PartialPathNodeFwd mid, PartialAccessPath ap, TypedContent tc, NodeEx node, CallContext cc, + Configuration config + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + read(midNode, tc.getContent(), node, pragma[only_bind_into](config)) and + ap.getHead() = tc and + pragma[only_bind_into](config) = mid.getConfiguration() and + cc = mid.getCallContext() + ) + } + + private predicate partialPathOutOfCallable0( + PartialPathNodeFwd mid, ReturnPosition pos, FlowState state, CallContext innercc, + PartialAccessPath ap, Configuration config + ) { + pos = mid.getNodeEx().(RetNodeEx).getReturnPosition() and + state = mid.getState() and + innercc = mid.getCallContext() and + innercc instanceof CallContextNoCall and + ap = mid.getAp() and + config = mid.getConfiguration() + } + + pragma[nomagic] + private predicate partialPathOutOfCallable1( + PartialPathNodeFwd mid, DataFlowCall call, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(ReturnPosition pos, DataFlowCallable c, CallContext innercc | + partialPathOutOfCallable0(mid, pos, state, innercc, ap, config) and + c = pos.getCallable() and + kind = pos.getKind() and + resolveReturn(innercc, c, call) + | + if reducedViableImplInReturn(c, call) then cc = TReturn(c, call) else cc = TAnyCallContext() + ) + } + + private predicate partialPathOutOfCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(ReturnKindExt kind, DataFlowCall call | + partialPathOutOfCallable1(mid, call, kind, state, cc, ap, config) + | + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[noinline] + private predicate partialPathIntoArg( + PartialPathNodeFwd mid, ParameterPosition ppos, FlowState state, CallContext cc, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ArgNode arg, ArgumentPosition apos | + arg = mid.getNodeEx().asNode() and + state = mid.getState() and + cc = mid.getCallContext() and + arg.argumentOf(call, apos) and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate partialPathIntoCallable0( + PartialPathNodeFwd mid, DataFlowCallable callable, ParameterPosition pos, FlowState state, + CallContext outercc, DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + partialPathIntoArg(mid, pos, state, outercc, call, ap, config) and + callable = resolveCall(call, outercc) + } + + private predicate partialPathIntoCallable( + PartialPathNodeFwd mid, ParamNodeEx p, FlowState state, CallContext outercc, + CallContextCall innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3, + DataFlowCall call, PartialAccessPath ap, Configuration config + ) { + exists(ParameterPosition pos, DataFlowCallable callable | + partialPathIntoCallable0(mid, callable, pos, state, outercc, call, ap, config) and + p.isParameterOf(callable, pos) and + sc1 = TSummaryCtx1Param(p) and + sc2 = TSummaryCtx2Some(state) and + sc3 = TSummaryCtx3Some(ap) + | + if recordDataFlowCallSite(call, callable) + then innercc = TSpecificCall(call) + else innercc = TSomeCall() + ) + } + + pragma[nomagic] + private predicate paramFlowsThroughInPartialPath( + ReturnKindExt kind, FlowState state, CallContextCall cc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, + TSummaryCtx3 sc3, PartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeFwd mid, RetNodeEx ret | + mid.getNodeEx() = ret and + kind = ret.getKind() and + state = mid.getState() and + cc = mid.getCallContext() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() and + ap = mid.getAp() + ) + } + + pragma[noinline] + private predicate partialPathThroughCallable0( + DataFlowCall call, PartialPathNodeFwd mid, ReturnKindExt kind, FlowState state, CallContext cc, + PartialAccessPath ap, Configuration config + ) { + exists(CallContext innercc, TSummaryCtx1 sc1, TSummaryCtx2 sc2, TSummaryCtx3 sc3 | + partialPathIntoCallable(mid, _, _, cc, innercc, sc1, sc2, sc3, call, _, config) and + paramFlowsThroughInPartialPath(kind, state, innercc, sc1, sc2, sc3, ap, config) + ) + } + + private predicate partialPathThroughCallable( + PartialPathNodeFwd mid, NodeEx out, FlowState state, CallContext cc, PartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ReturnKindExt kind | + partialPathThroughCallable0(call, mid, kind, state, cc, ap, config) and + out.asNode() = kind.getAnOutNode(call) + ) + } + + pragma[nomagic] + private predicate revPartialPathStep( + PartialPathNodeRev mid, NodeEx node, FlowState state, TRevSummaryCtx1 sc1, TRevSummaryCtx2 sc2, + TRevSummaryCtx3 sc3, RevPartialAccessPath ap, Configuration config + ) { + localFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalLocalFlowStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalLocalStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + jumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + or + additionalJumpStep(node, mid.getNodeEx(), config) and + state = mid.getState() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + additionalJumpStateStep(node, state, mid.getNodeEx(), mid.getState(), config) and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + mid.getAp() instanceof RevPartialAccessPathNil and + ap = TRevPartialNil() and + config = mid.getConfiguration() + or + revPartialPathReadStep(mid, _, _, node, ap) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + config = mid.getConfiguration() + or + exists(RevPartialAccessPath ap0, Content c | + revPartialPathStoreStep(mid, ap0, c, node, config) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + apConsRev(ap, c, ap0, config) + ) + or + exists(ParamNodeEx p | + mid.getNodeEx() = p and + viableParamArgEx(_, p, node) and + state = mid.getState() and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + sc1 = TRevSummaryCtx1None() and + sc2 = TRevSummaryCtx2None() and + sc3 = TRevSummaryCtx3None() and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + or + exists(ReturnPosition pos | + revPartialPathIntoReturn(mid, pos, state, sc1, sc2, sc3, _, ap, config) and + pos = getReturnPosition(node.asNode()) + ) + or + revPartialPathThroughCallable(mid, node, state, ap, config) and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() + } + + pragma[inline] + private predicate revPartialPathReadStep( + PartialPathNodeRev mid, RevPartialAccessPath ap1, Content c, NodeEx node, + RevPartialAccessPath ap2 + ) { + exists(NodeEx midNode | + midNode = mid.getNodeEx() and + ap1 = mid.getAp() and + read(node, c, midNode, mid.getConfiguration()) and + ap2.getHead() = c and + ap2.len() = unbindInt(ap1.len() + 1) + ) + } + + pragma[nomagic] + private predicate apConsRev( + RevPartialAccessPath ap1, Content c, RevPartialAccessPath ap2, Configuration config + ) { + exists(PartialPathNodeRev mid | + revPartialPathReadStep(mid, ap1, c, _, ap2) and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathStoreStep( + PartialPathNodeRev mid, RevPartialAccessPath ap, Content c, NodeEx node, Configuration config + ) { + exists(NodeEx midNode, TypedContent tc | + midNode = mid.getNodeEx() and + ap = mid.getAp() and + store(node, tc, midNode, _, config) and + ap.getHead() = c and + config = mid.getConfiguration() and + tc.getContent() = c + ) + } + + pragma[nomagic] + private predicate revPartialPathIntoReturn( + PartialPathNodeRev mid, ReturnPosition pos, FlowState state, TRevSummaryCtx1Some sc1, + TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3, DataFlowCall call, RevPartialAccessPath ap, + Configuration config + ) { + exists(NodeEx out | + mid.getNodeEx() = out and + mid.getState() = state and + viableReturnPosOutEx(call, pos, out) and + sc1 = TRevSummaryCtx1Some(pos) and + sc2 = TRevSummaryCtx2Some(state) and + sc3 = TRevSummaryCtx3Some(ap) and + ap = mid.getAp() and + config = mid.getConfiguration() + ) + } + + pragma[nomagic] + private predicate revPartialPathFlowsThrough( + ArgumentPosition apos, FlowState state, TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, + TRevSummaryCtx3Some sc3, RevPartialAccessPath ap, Configuration config + ) { + exists(PartialPathNodeRev mid, ParamNodeEx p, ParameterPosition ppos | + mid.getNodeEx() = p and + mid.getState() = state and + p.getPosition() = ppos and + sc1 = mid.getSummaryCtx1() and + sc2 = mid.getSummaryCtx2() and + sc3 = mid.getSummaryCtx3() and + ap = mid.getAp() and + config = mid.getConfiguration() and + parameterMatch(ppos, apos) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable0( + DataFlowCall call, PartialPathNodeRev mid, ArgumentPosition pos, FlowState state, + RevPartialAccessPath ap, Configuration config + ) { + exists(TRevSummaryCtx1Some sc1, TRevSummaryCtx2Some sc2, TRevSummaryCtx3Some sc3 | + revPartialPathIntoReturn(mid, _, _, sc1, sc2, sc3, call, _, config) and + revPartialPathFlowsThrough(pos, state, sc1, sc2, sc3, ap, config) + ) + } + + pragma[nomagic] + private predicate revPartialPathThroughCallable( + PartialPathNodeRev mid, ArgNodeEx node, FlowState state, RevPartialAccessPath ap, + Configuration config + ) { + exists(DataFlowCall call, ArgumentPosition pos | + revPartialPathThroughCallable0(call, mid, pos, state, ap, config) and + node.asNode().(ArgNode).argumentOf(call, pos) + ) + } +} + +import FlowExploration + +private predicate partialFlow( + PartialPathNode source, PartialPathNode node, Configuration configuration +) { + source.getConfiguration() = configuration and + source.isFwdSource() and + node = source.getASuccessor+() +} + +private predicate revPartialFlow( + PartialPathNode node, PartialPathNode sink, Configuration configuration +) { + sink.getConfiguration() = configuration and + sink.isRevSink() and + node.getASuccessor+() = sink +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll new file mode 100644 index 00000000000..95b34f15dad --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplCommon.qll @@ -0,0 +1,1374 @@ +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +import Cached + +module DataFlowImplCommonPublic { + /** A state value to track during data flow. */ + class FlowState = string; + + /** + * The default state, which is used when the state is unspecified for a source + * or a sink. + */ + class FlowStateEmpty extends FlowState { + FlowStateEmpty() { this = "" } + } + + private newtype TFlowFeature = + TFeatureHasSourceCallContext() or + TFeatureHasSinkCallContext() or + TFeatureEqualSourceSinkCallContext() + + /** A flow configuration feature for use in `Configuration::getAFeature()`. */ + class FlowFeature extends TFlowFeature { + string toString() { none() } + } + + /** + * A flow configuration feature that implies that sources have some existing + * call context. + */ + class FeatureHasSourceCallContext extends FlowFeature, TFeatureHasSourceCallContext { + override string toString() { result = "FeatureHasSourceCallContext" } + } + + /** + * A flow configuration feature that implies that sinks have some existing + * call context. + */ + class FeatureHasSinkCallContext extends FlowFeature, TFeatureHasSinkCallContext { + override string toString() { result = "FeatureHasSinkCallContext" } + } + + /** + * A flow configuration feature that implies that source-sink pairs have some + * shared existing call context. + */ + class FeatureEqualSourceSinkCallContext extends FlowFeature, TFeatureEqualSourceSinkCallContext { + override string toString() { result = "FeatureEqualSourceSinkCallContext" } + } +} + +/** + * The cost limits for the `AccessPathFront` to `AccessPathApprox` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathFront` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision during pruning. + */ +predicate accessPathApproxCostLimits(int apLimit, int tupleLimit) { + apLimit = 10 and + tupleLimit = 10000 +} + +/** + * The cost limits for the `AccessPathApprox` to `AccessPath` expansion. + * + * `apLimit` bounds the acceptable fan-out, and `tupleLimit` bounds the + * estimated per-`AccessPathApprox` tuple cost. Access paths exceeding both of + * these limits are represented with lower precision. + */ +predicate accessPathCostLimits(int apLimit, int tupleLimit) { + apLimit = 5 and + tupleLimit = 1000 +} + +/** + * Holds if `arg` is an argument of `call` with an argument position that matches + * parameter position `ppos`. + */ +pragma[noinline] +predicate argumentPositionMatch(DataFlowCall call, ArgNode arg, ParameterPosition ppos) { + exists(ArgumentPosition apos | + arg.argumentOf(call, apos) and + parameterMatch(ppos, apos) + ) +} + +/** + * Provides a simple data-flow analysis for resolving lambda calls. The analysis + * currently excludes read-steps, store-steps, and flow-through. + * + * The analysis uses non-linear recursion: When computing a flow path in or out + * of a call, we use the results of the analysis recursively to resolve lambda + * calls. For this reason, we cannot reuse the code from `DataFlowImpl.qll` directly. + */ +private module LambdaFlow { + pragma[noinline] + private predicate viableParamNonLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallable(call), ppos) + } + + pragma[noinline] + private predicate viableParamLambda(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallableLambda(call, _), ppos) + } + + private predicate viableParamArgNonLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParamNonLambda(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) + ) + } + + private predicate viableParamArgLambda(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParamLambda(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) + ) + } + + private newtype TReturnPositionSimple = + TReturnPositionSimple0(DataFlowCallable c, ReturnKind kind) { + exists(ReturnNode ret | + c = getNodeEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + pragma[noinline] + private TReturnPositionSimple getReturnPositionSimple(ReturnNode ret, ReturnKind kind) { + result = TReturnPositionSimple0(getNodeEnclosingCallable(ret), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosNonLambda(DataFlowCall call, ReturnKind kind) { + result = TReturnPositionSimple0(viableCallable(call), kind) + } + + pragma[nomagic] + private TReturnPositionSimple viableReturnPosLambda( + DataFlowCall call, DataFlowCallOption lastCall, ReturnKind kind + ) { + result = TReturnPositionSimple0(viableCallableLambda(call, lastCall), kind) + } + + private predicate viableReturnPosOutNonLambda( + DataFlowCall call, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosNonLambda(call, kind) and + out = getAnOutNode(call, kind) + ) + } + + private predicate viableReturnPosOutLambda( + DataFlowCall call, DataFlowCallOption lastCall, TReturnPositionSimple pos, OutNode out + ) { + exists(ReturnKind kind | + pos = viableReturnPosLambda(call, lastCall, kind) and + out = getAnOutNode(call, kind) + ) + } + + /** + * Holds if data can flow (inter-procedurally) from `node` (of type `t`) to + * the lambda call `lambdaCall`. + * + * The parameter `toReturn` indicates whether the path from `node` to + * `lambdaCall` goes through a return, and `toJump` whether the path goes + * through a jump step. + * + * The call context `lastCall` records the last call on the path from `node` + * to `lambdaCall`, if any. That is, `lastCall` is able to target the enclosing + * callable of `lambdaCall`. + */ + pragma[nomagic] + predicate revLambdaFlow( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + revLambdaFlow0(lambdaCall, kind, node, t, toReturn, toJump, lastCall) and + if castNode(node) or node instanceof ArgNode or node instanceof ReturnNode + then compatibleTypes(t, getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + predicate revLambdaFlow0( + DataFlowCall lambdaCall, LambdaCallKind kind, Node node, DataFlowType t, boolean toReturn, + boolean toJump, DataFlowCallOption lastCall + ) { + lambdaCall(lambdaCall, kind, node) and + t = getNodeDataFlowType(node) and + toReturn = false and + toJump = false and + lastCall = TDataFlowCallNone() + or + // local flow + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, toReturn, toJump, lastCall) + | + simpleLocalFlowStep(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) = getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // jump step + exists(Node mid, DataFlowType t0 | + revLambdaFlow(lambdaCall, kind, mid, t0, _, _, lastCall) and + toReturn = false and + toJump = true + | + jumpStepCached(node, mid) and + t = t0 + or + exists(boolean preservesValue | + additionalLambdaFlowStep(node, mid, preservesValue) and + getNodeEnclosingCallable(node) != getNodeEnclosingCallable(mid) + | + preservesValue = false and + t = getNodeDataFlowType(node) + or + preservesValue = true and + t = t0 + ) + ) + or + // flow into a callable + exists(ParamNode p, DataFlowCallOption lastCall0, DataFlowCall call | + revLambdaFlowIn(lambdaCall, kind, p, t, toJump, lastCall0) and + ( + if lastCall0 = TDataFlowCallNone() and toJump = false + then lastCall = TDataFlowCallSome(call) + else lastCall = lastCall0 + ) and + toReturn = false + | + viableParamArgNonLambda(call, p, node) + or + viableParamArgLambda(call, p, node) // non-linear recursion + ) + or + // flow out of a callable + exists(TReturnPositionSimple pos | + revLambdaFlowOut(lambdaCall, kind, pos, t, toJump, lastCall) and + getReturnPositionSimple(node, node.(ReturnNode).getKind()) = pos and + toReturn = true + ) + } + + pragma[nomagic] + predicate revLambdaFlowOutLambdaCall( + DataFlowCall lambdaCall, LambdaCallKind kind, OutNode out, DataFlowType t, boolean toJump, + DataFlowCall call, DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + exists(ReturnKindExt rk | + out = rk.getAnOutNode(call) and + lambdaCall(call, _, _) + ) + } + + pragma[nomagic] + predicate revLambdaFlowOut( + DataFlowCall lambdaCall, LambdaCallKind kind, TReturnPositionSimple pos, DataFlowType t, + boolean toJump, DataFlowCallOption lastCall + ) { + exists(DataFlowCall call, OutNode out | + revLambdaFlow(lambdaCall, kind, out, t, _, toJump, lastCall) and + viableReturnPosOutNonLambda(call, pos, out) + or + // non-linear recursion + revLambdaFlowOutLambdaCall(lambdaCall, kind, out, t, toJump, call, lastCall) and + viableReturnPosOutLambda(call, _, pos, out) + ) + } + + pragma[nomagic] + predicate revLambdaFlowIn( + DataFlowCall lambdaCall, LambdaCallKind kind, ParamNode p, DataFlowType t, boolean toJump, + DataFlowCallOption lastCall + ) { + revLambdaFlow(lambdaCall, kind, p, t, false, toJump, lastCall) + } +} + +private DataFlowCallable viableCallableExt(DataFlowCall call) { + result = viableCallable(call) + or + result = viableCallableLambda(call, _) +} + +cached +private module Cached { + /** + * If needed, call this predicate from `DataFlowImplSpecific.qll` in order to + * force a stage-dependency on the `DataFlowImplCommon.qll` stage and thereby + * collapsing the two stages. + */ + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate nodeEnclosingCallable(Node n, DataFlowCallable c) { c = nodeGetEnclosingCallable(n) } + + cached + predicate callEnclosingCallable(DataFlowCall call, DataFlowCallable c) { + c = call.getEnclosingCallable() + } + + cached + predicate nodeDataFlowType(Node n, DataFlowType t) { t = getNodeType(n) } + + cached + predicate jumpStepCached(Node node1, Node node2) { jumpStep(node1, node2) } + + cached + predicate clearsContentCached(Node n, ContentSet c) { clearsContent(n, c) } + + cached + predicate expectsContentCached(Node n, ContentSet c) { expectsContent(n, c) } + + cached + predicate isUnreachableInCallCached(Node n, DataFlowCall call) { isUnreachableInCall(n, call) } + + cached + predicate outNodeExt(Node n) { + n instanceof OutNode + or + n.(PostUpdateNode).getPreUpdateNode() instanceof ArgNode + } + + cached + predicate hiddenNode(Node n) { nodeIsHidden(n) } + + cached + OutNodeExt getAnOutNodeExt(DataFlowCall call, ReturnKindExt k) { + result = getAnOutNode(call, k.(ValueReturnKind).getKind()) + or + exists(ArgNode arg | + result.(PostUpdateNode).getPreUpdateNode() = arg and + arg.argumentOf(call, k.(ParamUpdateReturnKind).getAMatchingArgumentPosition()) + ) + } + + cached + predicate returnNodeExt(Node n, ReturnKindExt k) { + k = TValueReturn(n.(ReturnNode).getKind()) + or + exists(ParamNode p, ParameterPosition pos | + parameterValueFlowsToPreUpdate(p, n) and + p.isParameterOf(_, pos) and + k = TParamUpdate(pos) + ) + } + + cached + predicate castNode(Node n) { n instanceof CastNode } + + cached + predicate castingNode(Node n) { + castNode(n) or + n instanceof ParamNode or + n instanceof OutNodeExt or + // For reads, `x.f`, we want to check that the tracked type after the read (which + // is obtained by popping the head of the access path stack) is compatible with + // the type of `x.f`. + readSet(_, _, n) + } + + cached + predicate parameterNode(Node p, DataFlowCallable c, ParameterPosition pos) { + isParameterNode(p, c, pos) + } + + cached + predicate argumentNode(Node n, DataFlowCall call, ArgumentPosition pos) { + isArgumentNode(n, call, pos) + } + + /** + * Gets a viable target for the lambda call `call`. + * + * `lastCall` records the call required to reach `call` in order for the result + * to be a viable target, if any. + */ + cached + DataFlowCallable viableCallableLambda(DataFlowCall call, DataFlowCallOption lastCall) { + exists(Node creation, LambdaCallKind kind | + LambdaFlow::revLambdaFlow(call, kind, creation, _, _, _, lastCall) and + lambdaCreation(creation, kind, result) + ) + } + + /** + * Holds if `p` is the parameter of a viable dispatch target of `call`, + * and `p` has position `ppos`. + */ + pragma[nomagic] + private predicate viableParam(DataFlowCall call, ParameterPosition ppos, ParamNode p) { + p.isParameterOf(viableCallableExt(call), ppos) + } + + /** + * Holds if `arg` is a possible argument to `p` in `call`, taking virtual + * dispatch into account. + */ + cached + predicate viableParamArg(DataFlowCall call, ParamNode p, ArgNode arg) { + exists(ParameterPosition ppos | + viableParam(call, ppos, p) and + argumentPositionMatch(call, arg, ppos) and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(p)) + ) + } + + pragma[nomagic] + private ReturnPosition viableReturnPos(DataFlowCall call, ReturnKindExt kind) { + viableCallableExt(call) = result.getCallable() and + kind = result.getKind() + } + + /** + * Holds if a value at return position `pos` can be returned to `out` via `call`, + * taking virtual dispatch into account. + */ + cached + predicate viableReturnPosOut(DataFlowCall call, ReturnPosition pos, Node out) { + exists(ReturnKindExt kind | + pos = viableReturnPos(call, kind) and + out = kind.getAnOutNode(call) + ) + } + + /** Provides predicates for calculating flow-through summaries. */ + private module FlowThrough { + /** + * The first flow-through approximation: + * + * - Input access paths are abstracted with a Boolean parameter + * that indicates (non-)emptiness. + */ + private module Cand { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps. + * + * `read` indicates whether it is contents of `p` that can flow to `node`. + */ + pragma[nomagic] + private predicate parameterValueFlowCand(ParamNode p, Node node, boolean read) { + p = node and + read = false + or + // local flow + exists(Node mid | + parameterValueFlowCand(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlowCand(p, mid, false) and + readSet(mid, _, node) and + read = true + ) + or + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, false) and + argumentValueFlowsThroughCand(arg, node, read) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArgCand(p, arg, read) and + argumentValueFlowsThroughCand(arg, node, false) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArgCand(ParamNode p, ArgNode arg, boolean read) { + parameterValueFlowCand(p, arg, read) + } + + pragma[nomagic] + predicate parameterValueFlowsToPreUpdateCand(ParamNode p, PostUpdateNode n) { + parameterValueFlowCand(p, n.getPreUpdateNode(), false) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps, not taking call contexts + * into account. + * + * `read` indicates whether it is contents of `p` that can flow to the return + * node. + */ + predicate parameterValueFlowReturnCand(ParamNode p, ReturnKind kind, boolean read) { + exists(ReturnNode ret | + parameterValueFlowCand(p, ret, read) and + kind = ret.getKind() + ) + } + + pragma[nomagic] + private predicate argumentValueFlowsThroughCand0( + DataFlowCall call, ArgNode arg, ReturnKind kind, boolean read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturnCand(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only value-preserving steps, + * not taking call contexts into account. + * + * `read` indicates whether it is contents of `arg` that can flow to `out`. + */ + predicate argumentValueFlowsThroughCand(ArgNode arg, Node out, boolean read) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThroughCand0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + ) + } + + predicate cand(ParamNode p, Node n) { + parameterValueFlowCand(p, n, _) and + ( + parameterValueFlowReturnCand(p, _, _) + or + parameterValueFlowsToPreUpdateCand(p, _) + ) + } + } + + /** + * The final flow-through calculation: + * + * - Calculated flow is either value-preserving (`read = TReadStepTypesNone()`) + * or summarized as a single read step with before and after types recorded + * in the `ReadStepTypesOption` parameter. + * - Types are checked using the `compatibleTypes()` relation. + */ + private module Final { + /** + * Holds if `p` can flow to `node` in the same callable using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + predicate parameterValueFlow(ParamNode p, Node node, ReadStepTypesOption read) { + parameterValueFlow0(p, node, read) and + if node instanceof CastingNode + then + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(p), getNodeDataFlowType(node)) + or + // getter + compatibleTypes(read.getContentType(), getNodeDataFlowType(node)) + else any() + } + + pragma[nomagic] + private predicate parameterValueFlow0(ParamNode p, Node node, ReadStepTypesOption read) { + p = node and + Cand::cand(p, _) and + read = TReadStepTypesNone() + or + // local flow + exists(Node mid | + parameterValueFlow(p, mid, read) and + simpleLocalFlowStep(mid, node) + ) + or + // read + exists(Node mid | + parameterValueFlow(p, mid, TReadStepTypesNone()) and + readStepWithTypes(mid, read.getContainerType(), read.getContent(), node, + read.getContentType()) and + Cand::parameterValueFlowReturnCand(p, _, true) and + compatibleTypes(getNodeDataFlowType(p), read.getContainerType()) + ) + or + parameterValueFlow0_0(TReadStepTypesNone(), p, node, read) + } + + pragma[nomagic] + private predicate parameterValueFlow0_0( + ReadStepTypesOption mustBeNone, ParamNode p, Node node, ReadStepTypesOption read + ) { + // flow through: no prior read + exists(ArgNode arg | + parameterValueFlowArg(p, arg, mustBeNone) and + argumentValueFlowsThrough(arg, read, node) + ) + or + // flow through: no read inside method + exists(ArgNode arg | + parameterValueFlowArg(p, arg, read) and + argumentValueFlowsThrough(arg, mustBeNone, node) + ) + } + + pragma[nomagic] + private predicate parameterValueFlowArg(ParamNode p, ArgNode arg, ReadStepTypesOption read) { + parameterValueFlow(p, arg, read) and + Cand::argumentValueFlowsThroughCand(arg, _, _) + } + + pragma[nomagic] + private predicate argumentValueFlowsThrough0( + DataFlowCall call, ArgNode arg, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ParamNode param | viableParamArg(call, param, arg) | + parameterValueFlowReturn(param, kind, read) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and possibly a single read step, not taking + * call contexts into account. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + pragma[nomagic] + predicate argumentValueFlowsThrough(ArgNode arg, ReadStepTypesOption read, Node out) { + exists(DataFlowCall call, ReturnKind kind | + argumentValueFlowsThrough0(call, arg, kind, read) and + out = getAnOutNode(call, kind) + | + // normal flow through + read = TReadStepTypesNone() and + compatibleTypes(getNodeDataFlowType(arg), getNodeDataFlowType(out)) + or + // getter + compatibleTypes(getNodeDataFlowType(arg), read.getContainerType()) and + compatibleTypes(read.getContentType(), getNodeDataFlowType(out)) + ) + } + + /** + * Holds if `arg` flows to `out` through a call using only + * value-preserving steps and a single read step, not taking call + * contexts into account, thus representing a getter-step. + * + * This predicate is exposed for testing only. + */ + predicate getterStep(ArgNode arg, ContentSet c, Node out) { + argumentValueFlowsThrough(arg, TReadStepTypesSome(_, c, _), out) + } + + /** + * Holds if `p` can flow to a return node of kind `kind` in the same + * callable using only value-preserving steps and possibly a single read + * step. + * + * If a read step was taken, then `read` captures the `Content`, the + * container type, and the content type. + */ + private predicate parameterValueFlowReturn( + ParamNode p, ReturnKind kind, ReadStepTypesOption read + ) { + exists(ReturnNode ret | + parameterValueFlow(p, ret, read) and + kind = ret.getKind() + ) + } + } + + import Final + } + + import FlowThrough + + cached + private module DispatchWithCallContext { + /** + * Holds if the set of viable implementations that can be called by `call` + * might be improved by knowing the call context. + */ + pragma[nomagic] + private predicate mayBenefitFromCallContextExt(DataFlowCall call, DataFlowCallable callable) { + mayBenefitFromCallContext(call, callable) + or + callEnclosingCallable(call, callable) and + exists(viableCallableLambda(call, TDataFlowCallSome(_))) + } + + /** + * Gets a viable dispatch target of `call` in the context `ctx`. This is + * restricted to those `call`s for which a context might make a difference. + */ + pragma[nomagic] + private DataFlowCallable viableImplInCallContextExt(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContext(call, ctx) + or + result = viableCallableLambda(call, TDataFlowCallSome(ctx)) + or + exists(DataFlowCallable enclosing | + mayBenefitFromCallContextExt(call, enclosing) and + enclosing = viableCallableExt(ctx) and + result = viableCallableLambda(call, TDataFlowCallNone()) + ) + } + + /** + * Holds if the call context `ctx` reduces the set of viable run-time + * dispatch targets of call `call` in `c`. + */ + cached + predicate reducedViableImplInCallContext(DataFlowCall call, DataFlowCallable c, DataFlowCall ctx) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, c) and + c = viableCallableExt(ctx) and + ctxtgts = count(viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(viableCallableExt(call)) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls for which a context + * makes a difference. + */ + cached + DataFlowCallable prunedViableImplInCallContext(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInCallContext(call, _, ctx) + } + + /** + * Holds if flow returning from callable `c` to call `call` might return + * further and if this path restricts the set of call sites that can be + * returned to. + */ + cached + predicate reducedViableImplInReturn(DataFlowCallable c, DataFlowCall call) { + exists(int tgts, int ctxtgts | + mayBenefitFromCallContextExt(call, _) and + c = viableCallableExt(call) and + ctxtgts = count(DataFlowCall ctx | c = viableImplInCallContextExt(call, ctx)) and + tgts = strictcount(DataFlowCall ctx | callEnclosingCallable(call, viableCallableExt(ctx))) and + ctxtgts < tgts + ) + } + + /** + * Gets a viable run-time dispatch target for the call `call` in the + * context `ctx`. This is restricted to those calls and results for which + * the return flow from the result to `call` restricts the possible context + * `ctx`. + */ + cached + DataFlowCallable prunedViableImplInCallContextReverse(DataFlowCall call, DataFlowCall ctx) { + result = viableImplInCallContextExt(call, ctx) and + reducedViableImplInReturn(result, call) + } + } + + import DispatchWithCallContext + + /** + * Holds if `p` can flow to the pre-update node associated with post-update + * node `n`, in the same callable, using only value-preserving steps. + */ + private predicate parameterValueFlowsToPreUpdate(ParamNode p, PostUpdateNode n) { + parameterValueFlow(p, n.getPreUpdateNode(), TReadStepTypesNone()) + } + + cached + predicate readSet(Node node1, ContentSet c, Node node2) { readStep(node1, c, node2) } + + cached + predicate storeSet( + Node node1, ContentSet c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + storeStep(node1, c, node2) and + contentType = getNodeDataFlowType(node1) and + containerType = getNodeDataFlowType(node2) + or + exists(Node n1, Node n2 | + n1 = node1.(PostUpdateNode).getPreUpdateNode() and + n2 = node2.(PostUpdateNode).getPreUpdateNode() + | + argumentValueFlowsThrough(n2, TReadStepTypesSome(containerType, c, contentType), n1) + or + readSet(n2, c, n1) and + contentType = getNodeDataFlowType(n1) and + containerType = getNodeDataFlowType(n2) + ) + } + + private predicate store( + Node node1, Content c, Node node2, DataFlowType contentType, DataFlowType containerType + ) { + exists(ContentSet cs | + c = cs.getAStoreContent() and storeSet(node1, cs, node2, contentType, containerType) + ) + } + + /** + * Holds if data can flow from `node1` to `node2` via a direct assignment to + * `f`. + * + * This includes reverse steps through reads when the result of the read has + * been stored into, in order to handle cases like `x.f1.f2 = y`. + */ + cached + predicate store(Node node1, TypedContent tc, Node node2, DataFlowType contentType) { + store(node1, tc.getContent(), node2, contentType, tc.getContainerType()) + } + + /** + * Holds if data can flow from `fromNode` to `toNode` because they are the post-update + * nodes of some function output and input respectively, where the output and input + * are aliases. A typical example is a function returning `this`, implementing a fluent + * interface. + */ + private predicate reverseStepThroughInputOutputAlias( + PostUpdateNode fromNode, PostUpdateNode toNode + ) { + exists(Node fromPre, Node toPre | + fromPre = fromNode.getPreUpdateNode() and + toPre = toNode.getPreUpdateNode() + | + exists(DataFlowCall c | + // Does the language-specific simpleLocalFlowStep already model flow + // from function input to output? + fromPre = getAnOutNode(c, _) and + toPre.(ArgNode).argumentOf(c, _) and + simpleLocalFlowStep(toPre.(ArgNode), fromPre) + ) + or + argumentValueFlowsThrough(toPre, TReadStepTypesNone(), fromPre) + ) + } + + cached + predicate simpleLocalFlowStepExt(Node node1, Node node2) { + simpleLocalFlowStep(node1, node2) or + reverseStepThroughInputOutputAlias(node1, node2) + } + + /** + * Holds if the call context `call` improves virtual dispatch in `callable`. + */ + cached + predicate recordDataFlowCallSiteDispatch(DataFlowCall call, DataFlowCallable callable) { + reducedViableImplInCallContext(_, callable, call) + } + + /** + * Holds if the call context `call` allows us to prune unreachable nodes in `callable`. + */ + cached + predicate recordDataFlowCallSiteUnreachable(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable | isUnreachableInCallCached(n, call)) + } + + cached + predicate allowParameterReturnInSelfCached(ParamNode p) { allowParameterReturnInSelf(p) } + + cached + newtype TCallContext = + TAnyCallContext() or + TSpecificCall(DataFlowCall call) { recordDataFlowCallSite(call, _) } or + TSomeCall() or + TReturn(DataFlowCallable c, DataFlowCall call) { reducedViableImplInReturn(c, call) } + + cached + newtype TReturnPosition = + TReturnPosition0(DataFlowCallable c, ReturnKindExt kind) { + exists(ReturnNodeExt ret | + c = returnNodeGetEnclosingCallable(ret) and + kind = ret.getKind() + ) + } + + cached + newtype TLocalFlowCallContext = + TAnyLocalCall() or + TSpecificLocalCall(DataFlowCall call) { isUnreachableInCallCached(_, call) } + + cached + newtype TReturnKindExt = + TValueReturn(ReturnKind kind) or + TParamUpdate(ParameterPosition pos) { exists(ParamNode p | p.isParameterOf(_, pos)) } + + cached + newtype TBooleanOption = + TBooleanNone() or + TBooleanSome(boolean b) { b = true or b = false } + + cached + newtype TDataFlowCallOption = + TDataFlowCallNone() or + TDataFlowCallSome(DataFlowCall call) + + cached + newtype TTypedContent = MkTypedContent(Content c, DataFlowType t) { store(_, c, _, _, t) } + + cached + newtype TAccessPathFront = + TFrontNil(DataFlowType t) or + TFrontHead(TypedContent tc) + + cached + newtype TAccessPathFrontOption = + TAccessPathFrontNone() or + TAccessPathFrontSome(AccessPathFront apf) +} + +/** + * Holds if the call context `call` either improves virtual dispatch in + * `callable` or if it allows us to prune unreachable nodes in `callable`. + */ +predicate recordDataFlowCallSite(DataFlowCall call, DataFlowCallable callable) { + recordDataFlowCallSiteDispatch(call, callable) or + recordDataFlowCallSiteUnreachable(call, callable) +} + +/** + * A `Node` at which a cast can occur such that the type should be checked. + */ +class CastingNode extends Node { + CastingNode() { castingNode(this) } +} + +private predicate readStepWithTypes( + Node n1, DataFlowType container, ContentSet c, Node n2, DataFlowType content +) { + readSet(n1, c, n2) and + container = getNodeDataFlowType(n1) and + content = getNodeDataFlowType(n2) +} + +private newtype TReadStepTypesOption = + TReadStepTypesNone() or + TReadStepTypesSome(DataFlowType container, ContentSet c, DataFlowType content) { + readStepWithTypes(_, container, c, _, content) + } + +private class ReadStepTypesOption extends TReadStepTypesOption { + predicate isSome() { this instanceof TReadStepTypesSome } + + DataFlowType getContainerType() { this = TReadStepTypesSome(result, _, _) } + + ContentSet getContent() { this = TReadStepTypesSome(_, result, _) } + + DataFlowType getContentType() { this = TReadStepTypesSome(_, _, result) } + + string toString() { if this.isSome() then result = "Some(..)" else result = "None()" } +} + +/** + * A call context to restrict the targets of virtual dispatch, prune local flow, + * and match the call sites of flow into a method with flow out of a method. + * + * There are four cases: + * - `TAnyCallContext()` : No restrictions on method flow. + * - `TSpecificCall(DataFlowCall call)` : Flow entered through the + * given `call`. This call improves the set of viable + * dispatch targets for at least one method call in the current callable + * or helps prune unreachable nodes in the current callable. + * - `TSomeCall()` : Flow entered through a parameter. The + * originating call does not improve the set of dispatch targets for any + * method call in the current callable and was therefore not recorded. + * - `TReturn(Callable c, DataFlowCall call)` : Flow reached `call` from `c` and + * this dispatch target of `call` implies a reduced set of dispatch origins + * to which data may flow if it should reach a `return` statement. + */ +abstract class CallContext extends TCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); +} + +abstract class CallContextNoCall extends CallContext { } + +class CallContextAny extends CallContextNoCall, TAnyCallContext { + override string toString() { result = "CcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } +} + +abstract class CallContextCall extends CallContext { + /** Holds if this call context may be `call`. */ + bindingset[call] + abstract predicate matchesCall(DataFlowCall call); +} + +class CallContextSpecificCall extends CallContextCall, TSpecificCall { + override string toString() { + exists(DataFlowCall call | this = TSpecificCall(call) | result = "CcCall(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + recordDataFlowCallSite(this.getCall(), callable) + } + + override predicate matchesCall(DataFlowCall call) { call = this.getCall() } + + DataFlowCall getCall() { this = TSpecificCall(result) } +} + +class CallContextSomeCall extends CallContextCall, TSomeCall { + override string toString() { result = "CcSomeCall" } + + override predicate relevantFor(DataFlowCallable callable) { + exists(ParamNode p | getNodeEnclosingCallable(p) = callable) + } + + override predicate matchesCall(DataFlowCall call) { any() } +} + +class CallContextReturn extends CallContextNoCall, TReturn { + override string toString() { + exists(DataFlowCall call | this = TReturn(_, call) | result = "CcReturn(" + call + ")") + } + + override predicate relevantFor(DataFlowCallable callable) { + exists(DataFlowCall call | this = TReturn(_, call) and callEnclosingCallable(call, callable)) + } +} + +/** + * A call context that is relevant for pruning local flow. + */ +abstract class LocalCallContext extends TLocalFlowCallContext { + abstract string toString(); + + /** Holds if this call context is relevant for `callable`. */ + abstract predicate relevantFor(DataFlowCallable callable); +} + +class LocalCallContextAny extends LocalCallContext, TAnyLocalCall { + override string toString() { result = "LocalCcAny" } + + override predicate relevantFor(DataFlowCallable callable) { any() } +} + +class LocalCallContextSpecificCall extends LocalCallContext, TSpecificLocalCall { + LocalCallContextSpecificCall() { this = TSpecificLocalCall(call) } + + DataFlowCall call; + + DataFlowCall getCall() { result = call } + + override string toString() { result = "LocalCcCall(" + call + ")" } + + override predicate relevantFor(DataFlowCallable callable) { relevantLocalCCtx(call, callable) } +} + +private predicate relevantLocalCCtx(DataFlowCall call, DataFlowCallable callable) { + exists(Node n | getNodeEnclosingCallable(n) = callable and isUnreachableInCallCached(n, call)) +} + +/** + * Gets the local call context given the call context and the callable that + * the contexts apply to. + */ +LocalCallContext getLocalCallContext(CallContext ctx, DataFlowCallable callable) { + ctx.relevantFor(callable) and + if relevantLocalCCtx(ctx.(CallContextSpecificCall).getCall(), callable) + then result.(LocalCallContextSpecificCall).getCall() = ctx.(CallContextSpecificCall).getCall() + else result instanceof LocalCallContextAny +} + +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. + */ +class ParamNode extends Node { + ParamNode() { parameterNode(this, _, _) } + + /** + * Holds if this node is the parameter of callable `c` at the specified + * position. + */ + predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { parameterNode(this, c, pos) } +} + +/** A data-flow node that represents a call argument. */ +class ArgNode extends Node { + ArgNode() { argumentNode(this, _, _) } + + /** Holds if this argument occurs at the given position in the given call. */ + final predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + argumentNode(this, call, pos) + } +} + +/** + * A node from which flow can return to the caller. This is either a regular + * `ReturnNode` or a `PostUpdateNode` corresponding to the value of a parameter. + */ +class ReturnNodeExt extends Node { + ReturnNodeExt() { returnNodeExt(this, _) } + + /** Gets the kind of this returned value. */ + ReturnKindExt getKind() { returnNodeExt(this, result) } +} + +/** + * A node to which data can flow from a call. Either an ordinary out node + * or a post-update node associated with a call argument. + */ +class OutNodeExt extends Node { + OutNodeExt() { outNodeExt(this) } +} + +/** + * An extended return kind. A return kind describes how data can be returned + * from a callable. This can either be through a returned value or an updated + * parameter. + */ +abstract class ReturnKindExt extends TReturnKindExt { + /** Gets a textual representation of this return kind. */ + abstract string toString(); + + /** Gets a node corresponding to data flow out of `call`. */ + final OutNodeExt getAnOutNode(DataFlowCall call) { result = getAnOutNodeExt(call, this) } +} + +class ValueReturnKind extends ReturnKindExt, TValueReturn { + private ReturnKind kind; + + ValueReturnKind() { this = TValueReturn(kind) } + + ReturnKind getKind() { result = kind } + + override string toString() { result = kind.toString() } +} + +class ParamUpdateReturnKind extends ReturnKindExt, TParamUpdate { + private ParameterPosition pos; + + ParamUpdateReturnKind() { this = TParamUpdate(pos) } + + ParameterPosition getPosition() { result = pos } + + pragma[nomagic] + ArgumentPosition getAMatchingArgumentPosition() { parameterMatch(pos, result) } + + override string toString() { result = "param update " + pos } +} + +/** A callable tagged with a relevant return kind. */ +class ReturnPosition extends TReturnPosition0 { + private DataFlowCallable c; + private ReturnKindExt kind; + + ReturnPosition() { this = TReturnPosition0(c, kind) } + + /** Gets the callable. */ + DataFlowCallable getCallable() { result = c } + + /** Gets the return kind. */ + ReturnKindExt getKind() { result = kind } + + /** Gets a textual representation of this return position. */ + string toString() { result = "[" + kind + "] " + c } +} + +/** + * Gets the enclosing callable of `n`. Unlike `n.getEnclosingCallable()`, this + * predicate ensures that joins go from `n` to the result instead of the other + * way around. + */ +pragma[inline] +DataFlowCallable getNodeEnclosingCallable(Node n) { + nodeEnclosingCallable(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +/** Gets the type of `n` used for type pruning. */ +pragma[inline] +DataFlowType getNodeDataFlowType(Node n) { + nodeDataFlowType(pragma[only_bind_out](n), pragma[only_bind_into](result)) +} + +pragma[noinline] +private DataFlowCallable returnNodeGetEnclosingCallable(ReturnNodeExt ret) { + result = getNodeEnclosingCallable(ret) +} + +pragma[noinline] +private ReturnPosition getReturnPosition0(ReturnNodeExt ret, ReturnKindExt kind) { + result.getCallable() = returnNodeGetEnclosingCallable(ret) and + kind = result.getKind() +} + +pragma[noinline] +ReturnPosition getReturnPosition(ReturnNodeExt ret) { + result = getReturnPosition0(ret, ret.getKind()) +} + +/** + * Checks whether `inner` can return to `call` in the call context `innercc`. + * Assumes a context of `inner = viableCallableExt(call)`. + */ +bindingset[innercc, inner, call] +predicate checkCallContextReturn(CallContext innercc, DataFlowCallable inner, DataFlowCall call) { + innercc instanceof CallContextAny + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, inner) and + innercc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) +} + +/** + * Checks whether `call` can resolve to `calltarget` in the call context `cc`. + * Assumes a context of `calltarget = viableCallableExt(call)`. + */ +bindingset[cc, call, calltarget] +predicate checkCallContextCall(CallContext cc, DataFlowCall call, DataFlowCallable calltarget) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then calltarget = prunedViableImplInCallContext(call, ctx) + else any() + ) + or + cc instanceof CallContextSomeCall + or + cc instanceof CallContextAny + or + cc instanceof CallContextReturn +} + +/** + * Resolves a return from `callable` in `cc` to `call`. This is equivalent to + * `callable = viableCallableExt(call) and checkCallContextReturn(cc, callable, call)`. + */ +bindingset[cc, callable] +predicate resolveReturn(CallContext cc, DataFlowCallable callable, DataFlowCall call) { + cc instanceof CallContextAny and callable = viableCallableExt(call) + or + exists(DataFlowCallable c0, DataFlowCall call0 | + callEnclosingCallable(call0, callable) and + cc = TReturn(c0, call0) and + c0 = prunedViableImplInCallContextReverse(call0, call) + ) +} + +/** + * Resolves a call from `call` in `cc` to `result`. This is equivalent to + * `result = viableCallableExt(call) and checkCallContextCall(cc, call, result)`. + */ +bindingset[call, cc] +DataFlowCallable resolveCall(DataFlowCall call, CallContext cc) { + exists(DataFlowCall ctx | cc = TSpecificCall(ctx) | + if reducedViableImplInCallContext(call, _, ctx) + then result = prunedViableImplInCallContext(call, ctx) + else result = viableCallableExt(call) + ) + or + result = viableCallableExt(call) and cc instanceof CallContextSomeCall + or + result = viableCallableExt(call) and cc instanceof CallContextAny + or + result = viableCallableExt(call) and cc instanceof CallContextReturn +} + +/** An optional Boolean value. */ +class BooleanOption extends TBooleanOption { + string toString() { + this = TBooleanNone() and result = "" + or + this = TBooleanSome(any(boolean b | result = b.toString())) + } +} + +/** An optional `DataFlowCall`. */ +class DataFlowCallOption extends TDataFlowCallOption { + string toString() { + this = TDataFlowCallNone() and + result = "(none)" + or + exists(DataFlowCall call | + this = TDataFlowCallSome(call) and + result = call.toString() + ) + } +} + +/** A `Content` tagged with the type of a containing object. */ +class TypedContent extends MkTypedContent { + private Content c; + private DataFlowType t; + + TypedContent() { this = MkTypedContent(c, t) } + + /** Gets the content. */ + Content getContent() { result = c } + + /** Gets the container type. */ + DataFlowType getContainerType() { result = t } + + /** Gets a textual representation of this content. */ + string toString() { result = c.toString() } + + /** + * Holds if access paths with this `TypedContent` at their head always should + * be tracked at high precision. This disables adaptive access path precision + * for such access paths. + */ + predicate forceHighPrecision() { forceHighPrecision(c) } +} + +/** + * The front of an access path. This is either a head or a nil. + */ +abstract class AccessPathFront extends TAccessPathFront { + abstract string toString(); + + abstract DataFlowType getType(); + + abstract boolean toBoolNonEmpty(); + + TypedContent getHead() { this = TFrontHead(result) } +} + +class AccessPathFrontNil extends AccessPathFront, TFrontNil { + private DataFlowType t; + + AccessPathFrontNil() { this = TFrontNil(t) } + + override string toString() { result = ppReprType(t) } + + override DataFlowType getType() { result = t } + + override boolean toBoolNonEmpty() { result = false } +} + +class AccessPathFrontHead extends AccessPathFront, TFrontHead { + private TypedContent tc; + + AccessPathFrontHead() { this = TFrontHead(tc) } + + override string toString() { result = tc.toString() } + + override DataFlowType getType() { result = tc.getContainerType() } + + override boolean toBoolNonEmpty() { result = true } +} + +/** An optional access path front. */ +class AccessPathFrontOption extends TAccessPathFrontOption { + string toString() { + this = TAccessPathFrontNone() and result = "" + or + this = TAccessPathFrontSome(any(AccessPathFront apf | result = apf.toString())) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll new file mode 100644 index 00000000000..d6c1bad2744 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplConsistency.qll @@ -0,0 +1,212 @@ +/** + * Provides consistency queries for checking invariants in the language-specific + * data-flow classes and predicates. + */ + +private import DataFlowImplSpecific::Private +private import DataFlowImplSpecific::Public +private import tainttracking1.TaintTrackingParameter::Private +private import tainttracking1.TaintTrackingParameter::Public + +module Consistency { + private newtype TConsistencyConfiguration = MkConsistencyConfiguration() + + /** A class for configuring the consistency queries. */ + class ConsistencyConfiguration extends TConsistencyConfiguration { + string toString() { none() } + + /** Holds if `n` should be excluded from the consistency test `uniqueEnclosingCallable`. */ + predicate uniqueEnclosingCallableExclude(Node n) { none() } + + /** Holds if `n` should be excluded from the consistency test `uniqueNodeLocation`. */ + predicate uniqueNodeLocationExclude(Node n) { none() } + + /** Holds if `n` should be excluded from the consistency test `missingLocation`. */ + predicate missingLocationExclude(Node n) { none() } + + /** Holds if `n` should be excluded from the consistency test `postWithInFlow`. */ + predicate postWithInFlowExclude(Node n) { none() } + + /** Holds if `n` should be excluded from the consistency test `argHasPostUpdate`. */ + predicate argHasPostUpdateExclude(ArgumentNode n) { none() } + + /** Holds if `n` should be excluded from the consistency test `reverseRead`. */ + predicate reverseReadExclude(Node n) { none() } + } + + private class RelevantNode extends Node { + RelevantNode() { + this instanceof ArgumentNode or + this instanceof ParameterNode or + this instanceof ReturnNode or + this = getAnOutNode(_, _) or + simpleLocalFlowStep(this, _) or + simpleLocalFlowStep(_, this) or + jumpStep(this, _) or + jumpStep(_, this) or + storeStep(this, _, _) or + storeStep(_, _, this) or + readStep(this, _, _) or + readStep(_, _, this) or + defaultAdditionalTaintStep(this, _) or + defaultAdditionalTaintStep(_, this) + } + } + + query predicate uniqueEnclosingCallable(Node n, string msg) { + exists(int c | + n instanceof RelevantNode and + c = count(nodeGetEnclosingCallable(n)) and + c != 1 and + not any(ConsistencyConfiguration conf).uniqueEnclosingCallableExclude(n) and + msg = "Node should have one enclosing callable but has " + c + "." + ) + } + + query predicate uniqueType(Node n, string msg) { + exists(int c | + n instanceof RelevantNode and + c = count(getNodeType(n)) and + c != 1 and + msg = "Node should have one type but has " + c + "." + ) + } + + query predicate uniqueNodeLocation(Node n, string msg) { + exists(int c | + c = + count(string filepath, int startline, int startcolumn, int endline, int endcolumn | + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) and + c != 1 and + not any(ConsistencyConfiguration conf).uniqueNodeLocationExclude(n) and + msg = "Node should have one location but has " + c + "." + ) + } + + query predicate missingLocation(string msg) { + exists(int c | + c = + strictcount(Node n | + not exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + n.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + ) and + not any(ConsistencyConfiguration conf).missingLocationExclude(n) + ) and + msg = "Nodes without location: " + c + ) + } + + query predicate uniqueNodeToString(Node n, string msg) { + exists(int c | + c = count(n.toString()) and + c != 1 and + msg = "Node should have one toString but has " + c + "." + ) + } + + query predicate missingToString(string msg) { + exists(int c | + c = strictcount(Node n | not exists(n.toString())) and + msg = "Nodes without toString: " + c + ) + } + + query predicate parameterCallable(ParameterNode p, string msg) { + exists(DataFlowCallable c | isParameterNode(p, c, _) and c != nodeGetEnclosingCallable(p)) and + msg = "Callable mismatch for parameter." + } + + query predicate localFlowIsLocal(Node n1, Node n2, string msg) { + simpleLocalFlowStep(n1, n2) and + nodeGetEnclosingCallable(n1) != nodeGetEnclosingCallable(n2) and + msg = "Local flow step does not preserve enclosing callable." + } + + private DataFlowType typeRepr() { result = getNodeType(_) } + + query predicate compatibleTypesReflexive(DataFlowType t, string msg) { + t = typeRepr() and + not compatibleTypes(t, t) and + msg = "Type compatibility predicate is not reflexive." + } + + query predicate unreachableNodeCCtx(Node n, DataFlowCall call, string msg) { + isUnreachableInCall(n, call) and + exists(DataFlowCallable c | + c = nodeGetEnclosingCallable(n) and + not viableCallable(call) = c + ) and + msg = "Call context for isUnreachableInCall is inconsistent with call graph." + } + + query predicate localCallNodes(DataFlowCall call, Node n, string msg) { + ( + n = getAnOutNode(call, _) and + msg = "OutNode and call does not share enclosing callable." + or + n.(ArgumentNode).argumentOf(call, _) and + msg = "ArgumentNode and call does not share enclosing callable." + ) and + nodeGetEnclosingCallable(n) != call.getEnclosingCallable() + } + + // This predicate helps the compiler forget that in some languages + // it is impossible for a result of `getPreUpdateNode` to be an + // instance of `PostUpdateNode`. + private Node getPre(PostUpdateNode n) { + result = n.getPreUpdateNode() + or + none() + } + + query predicate postIsNotPre(PostUpdateNode n, string msg) { + getPre(n) = n and + msg = "PostUpdateNode should not equal its pre-update node." + } + + query predicate postHasUniquePre(PostUpdateNode n, string msg) { + exists(int c | + c = count(n.getPreUpdateNode()) and + c != 1 and + msg = "PostUpdateNode should have one pre-update node but has " + c + "." + ) + } + + query predicate uniquePostUpdate(Node n, string msg) { + 1 < strictcount(PostUpdateNode post | post.getPreUpdateNode() = n) and + msg = "Node has multiple PostUpdateNodes." + } + + query predicate postIsInSameCallable(PostUpdateNode n, string msg) { + nodeGetEnclosingCallable(n) != nodeGetEnclosingCallable(n.getPreUpdateNode()) and + msg = "PostUpdateNode does not share callable with its pre-update node." + } + + private predicate hasPost(Node n) { exists(PostUpdateNode post | post.getPreUpdateNode() = n) } + + query predicate reverseRead(Node n, string msg) { + exists(Node n2 | readStep(n, _, n2) and hasPost(n2) and not hasPost(n)) and + not any(ConsistencyConfiguration conf).reverseReadExclude(n) and + msg = "Origin of readStep is missing a PostUpdateNode." + } + + query predicate argHasPostUpdate(ArgumentNode n, string msg) { + not hasPost(n) and + not any(ConsistencyConfiguration c).argHasPostUpdateExclude(n) and + msg = "ArgumentNode is missing PostUpdateNode." + } + + // This predicate helps the compiler forget that in some languages + // it is impossible for a `PostUpdateNode` to be the target of + // `simpleLocalFlowStep`. + private predicate isPostUpdateNode(Node n) { n instanceof PostUpdateNode or none() } + + query predicate postWithInFlow(Node n, string msg) { + isPostUpdateNode(n) and + not clearsContent(n, _) and + simpleLocalFlowStep(_, n) and + not any(ConsistencyConfiguration c).postWithInFlowExclude(n) and + msg = "PostUpdateNode should not be the target of local flow." + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll new file mode 100644 index 00000000000..4ea383b20a1 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowImplSpecific.qll @@ -0,0 +1,11 @@ +/** + * Provides IR-specific definitions for use in the data flow library. + */ +module Private { + import DataFlowPrivate + import DataFlowDispatch +} + +module Public { + import DataFlowUtil +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll new file mode 100644 index 00000000000..d6b2d455dd2 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -0,0 +1,560 @@ +private import cpp as Cpp +private import DataFlowUtil +private import semmle.code.cpp.ir.IR +private import DataFlowDispatch +private import DataFlowImplConsistency +private import semmle.code.cpp.ir.internal.IRCppLanguage +private import SsaInternals as Ssa + +/** Gets the callable in which this node occurs. */ +DataFlowCallable nodeGetEnclosingCallable(Node n) { result = n.getEnclosingCallable() } + +/** Holds if `p` is a `ParameterNode` of `c` with position `pos`. */ +predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { + p.isParameterOf(c, pos) +} + +/** Holds if `arg` is an `ArgumentNode` of `c` with position `pos`. */ +predicate isArgumentNode(ArgumentNode arg, DataFlowCall c, ArgumentPosition pos) { + arg.argumentOf(c, pos) +} + +/** + * A data flow node that occurs as the argument of a call and is passed as-is + * to the callable. Instance arguments (`this` pointer) and read side effects + * on parameters are also included. + */ +abstract class ArgumentNode extends Node { + /** + * Holds if this argument occurs at the given position in the given call. + * The instance argument is considered to have index `-1`. + */ + abstract predicate argumentOf(DataFlowCall call, ArgumentPosition pos); + + /** Gets the call in which this node is an argument. */ + DataFlowCall getCall() { this.argumentOf(result, _) } +} + +/** + * A data flow node that occurs as the argument to a call, or an + * implicit `this` pointer argument. + */ +private class PrimaryArgumentNode extends ArgumentNode, OperandNode { + override ArgumentOperand op; + + PrimaryArgumentNode() { exists(CallInstruction call | op = call.getAnArgumentOperand()) } + + override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + op = call.getArgumentOperand(pos.(DirectPosition).getIndex()) + } + + override string toStringImpl() { result = argumentOperandToString(op) } +} + +private string argumentOperandToString(ArgumentOperand op) { + exists(Expr unconverted | + unconverted = op.getDef().getUnconvertedResultExpression() and + result = unconverted.toString() + ) + or + // Certain instructions don't map to an unconverted result expression. For these cases + // we fall back to a simpler naming scheme. This can happen in IR-generated constructors. + not exists(op.getDef().getUnconvertedResultExpression()) and + ( + result = "Argument " + op.(PositionalArgumentOperand).getIndex() + or + op instanceof ThisArgumentOperand and result = "Argument this" + ) +} + +private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode { + override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) { + this.getCallInstruction() = dfCall and + pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and + pos.(IndirectionPosition).getIndirectionIndex() = super.getIndirectionIndex() + } + + override string toStringImpl() { + result = argumentOperandToString(this.getAddressOperand()) + " indirection" + } +} + +/** A parameter position represented by an integer. */ +class ParameterPosition = Position; + +/** An argument position represented by an integer. */ +class ArgumentPosition = Position; + +class Position extends TPosition { + abstract string toString(); +} + +class DirectPosition extends Position, TDirectPosition { + int index; + + DirectPosition() { this = TDirectPosition(index) } + + override string toString() { if index = -1 then result = "this" else result = index.toString() } + + int getIndex() { result = index } +} + +class IndirectionPosition extends Position, TIndirectionPosition { + int argumentIndex; + int indirectionIndex; + + IndirectionPosition() { this = TIndirectionPosition(argumentIndex, indirectionIndex) } + + override string toString() { + if argumentIndex = -1 + then if indirectionIndex > 0 then result = "this indirection" else result = "this" + else + if indirectionIndex > 0 + then result = argumentIndex.toString() + " indirection" + else result = argumentIndex.toString() + } + + int getArgumentIndex() { result = argumentIndex } + + int getIndirectionIndex() { result = indirectionIndex } +} + +newtype TPosition = + TDirectPosition(int index) { exists(any(CallInstruction c).getArgument(index)) } or + TIndirectionPosition(int argumentIndex, int indirectionIndex) { + hasOperandAndIndex(_, any(CallInstruction call).getArgumentOperand(argumentIndex), + indirectionIndex) + } + +private newtype TReturnKind = + TNormalReturnKind(int index) { + exists(IndirectReturnNode return | + return.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and + index = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value. + ) + } or + TIndirectReturnKind(int argumentIndex, int indirectionIndex) { + exists(IndirectReturnNode return, ReturnIndirectionInstruction returnInd | + returnInd.hasIndex(argumentIndex) and + return.getAddressOperand() = returnInd.getSourceAddressOperand() and + indirectionIndex = return.getIndirectionIndex() - 1 // We subtract one because the return loads the value. + ) + } + +/** + * A return kind. A return kind describes how a value can be returned + * from a callable. For C++, this is simply a function return. + */ +class ReturnKind extends TReturnKind { + /** Gets a textual representation of this return kind. */ + abstract string toString(); +} + +private class NormalReturnKind extends ReturnKind, TNormalReturnKind { + int index; + + NormalReturnKind() { this = TNormalReturnKind(index) } + + override string toString() { result = "indirect return" } +} + +private class IndirectReturnKind extends ReturnKind, TIndirectReturnKind { + int argumentIndex; + int indirectionIndex; + + IndirectReturnKind() { this = TIndirectReturnKind(argumentIndex, indirectionIndex) } + + override string toString() { result = "indirect outparam[" + argumentIndex.toString() + "]" } +} + +/** A data flow node that occurs as the result of a `ReturnStmt`. */ +class ReturnNode extends Node instanceof IndirectReturnNode { + /** Gets the kind of this returned value. */ + abstract ReturnKind getKind(); +} + +/** + * This predicate represents an annoying hack that we have to do. We use the + * `ReturnIndirectionInstruction` to determine which variables need flow back + * out of a function. However, the IR will unconditionally create those for a + * variable passed to a function even though the variable was never updated by + * the function. And if a function has too many `ReturnNode`s the dataflow + * library lowers its precision for that function by disabling field flow. + * + * So we those eliminate `ReturnNode`s that would have otherwise been created + * by this unconditional `ReturnIndirectionInstruction` by requiring that there + * must exist an SSA definition of the IR variable in the function. + */ +private predicate hasNonInitializeParameterDef(IRVariable v) { + exists(Ssa::Def def | + not def.getDefiningInstruction() instanceof InitializeParameterInstruction and + v = def.getSourceVariable().getBaseVariable().(Ssa::BaseIRVariable).getIRVariable() + ) +} + +class ReturnIndirectionNode extends IndirectReturnNode, ReturnNode { + override ReturnKind getKind() { + exists(int argumentIndex, ReturnIndirectionInstruction returnInd | + returnInd.hasIndex(argumentIndex) and + this.getAddressOperand() = returnInd.getSourceAddressOperand() and + result = TIndirectReturnKind(argumentIndex, this.getIndirectionIndex() - 1) and + hasNonInitializeParameterDef(returnInd.getIRVariable()) + ) + or + this.getAddressOperand() = any(ReturnValueInstruction r).getReturnAddressOperand() and + result = TNormalReturnKind(this.getIndirectionIndex() - 1) + } +} + +private Operand fullyConvertedCallStep(Operand op) { + not exists(getANonConversionUse(op)) and + exists(Instruction instr | + conversionFlow(op, instr, _) and + result = getAUse(instr) + ) +} + +/** + * Gets the instruction that uses this operand, if the instruction is not + * ignored for dataflow purposes. + */ +private Instruction getUse(Operand op) { + result = op.getUse() and + not Ssa::ignoreOperand(op) +} + +/** Gets a use of the instruction `instr` that is not ignored for dataflow purposes. */ +Operand getAUse(Instruction instr) { + result = instr.getAUse() and + not Ssa::ignoreOperand(result) +} + +/** + * Gets a use of `operand` that is: + * - not ignored for dataflow purposes, and + * - not a conversion-like instruction. + */ +private Instruction getANonConversionUse(Operand operand) { + result = getUse(operand) and + not conversionFlow(_, result, _) +} + +/** + * Gets the operand that represents the first use of the value of `call` following + * a sequnce of conversion-like instructions. + */ +predicate operandForfullyConvertedCall(Operand operand, CallInstruction call) { + exists(getANonConversionUse(operand)) and + ( + operand = getAUse(call) + or + operand = fullyConvertedCallStep*(getAUse(call)) + ) +} + +/** + * Gets the instruction that represents the first use of the value of `call` following + * a sequnce of conversion-like instructions. + * + * This predicate only holds if there is no suitable operand (i.e., no operand of a non- + * conversion instruction) to use to represent the value of `call` after conversions. + */ +predicate instructionForfullyConvertedCall(Instruction instr, CallInstruction call) { + not operandForfullyConvertedCall(_, call) and + ( + // If there is no use of the call then we pick the call instruction + not exists(getAUse(call)) and + instr = call + or + // Otherwise, flow to the first non-conversion use. + exists(Operand operand | operand = fullyConvertedCallStep*(getAUse(call)) | + instr = getANonConversionUse(operand) + ) + ) +} + +/** Holds if `node` represents the output node for `call`. */ +private predicate simpleOutNode(Node node, CallInstruction call) { + operandForfullyConvertedCall(node.asOperand(), call) + or + instructionForfullyConvertedCall(node.asInstruction(), call) +} + +/** A data flow node that represents the output of a call. */ +class OutNode extends Node { + OutNode() { + // Return values not hidden behind indirections + simpleOutNode(this, _) + or + // Return values hidden behind indirections + this instanceof IndirectReturnOutNode + or + // Modified arguments hidden behind indirections + this instanceof IndirectArgumentOutNode + } + + /** Gets the underlying call. */ + abstract DataFlowCall getCall(); + + abstract ReturnKind getReturnKind(); +} + +private class DirectCallOutNode extends OutNode { + CallInstruction call; + + DirectCallOutNode() { simpleOutNode(this, call) } + + override DataFlowCall getCall() { result = call } + + override ReturnKind getReturnKind() { result = TNormalReturnKind(0) } +} + +private class IndirectCallOutNode extends OutNode, IndirectReturnOutNode { + override DataFlowCall getCall() { result = this.getCallInstruction() } + + override ReturnKind getReturnKind() { result = TNormalReturnKind(this.getIndirectionIndex()) } +} + +private class SideEffectOutNode extends OutNode, IndirectArgumentOutNode { + override DataFlowCall getCall() { result = this.getCallInstruction() } + + override ReturnKind getReturnKind() { + result = TIndirectReturnKind(this.getArgumentIndex(), this.getIndirectionIndex()) + } +} + +/** + * Gets a node that can read the value returned from `call` with return kind + * `kind`. + */ +OutNode getAnOutNode(DataFlowCall call, ReturnKind kind) { + result.getCall() = call and + result.getReturnKind() = kind +} + +/** + * Holds if data can flow from `node1` to `node2` in a way that loses the + * calling context. For example, this would happen with flow through a + * global or static variable. + */ +predicate jumpStep(Node n1, Node n2) { + exists(Cpp::GlobalOrNamespaceVariable v | + v = + n1.asInstruction() + .(StoreInstruction) + .getResultAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n2.asVariable() + or + v = + n2.asInstruction() + .(LoadInstruction) + .getSourceAddress() + .(VariableAddressInstruction) + .getAstVariable() and + v = n1.asVariable() + ) +} + +/** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + */ +predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { + exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store | + nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and + node2.getIndirectionIndex() = 0 and + numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(), + numberOfLoads) + | + exists(FieldContent fc | fc = c | + fc.getField() = node2.getUpdatedField() and + fc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads + ) + or + exists(UnionContent uc | uc = c | + uc.getAField() = node2.getUpdatedField() and + uc.getIndirectionIndex() = 1 + indirectionIndex1 + numberOfLoads + ) + ) +} + +/** + * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like + * operations and exactly `n` `LoadInstruction` operations. + */ +private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) { + exists(LoadInstruction load | load.getSourceAddressOperand() = operandFrom | + operandTo = operandFrom and ind = 0 + or + numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1) + ) + or + exists(Operand op, Instruction instr | + instr = op.getDef() and + conversionFlow(operandFrom, instr, _) and + numberOfLoadsFromOperand(op, operandTo, ind) + ) +} + +/** + * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like + * operations and exactly `n` `LoadInstruction` operations. + */ +private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) { + numberOfLoadsFromOperandRec(operandFrom, operandTo, n) + or + not any(LoadInstruction load).getSourceAddressOperand() = operandFrom and + not conversionFlow(operandFrom, _, _) and + operandFrom = operandTo and + n = 0 +} + +// Needed to join on both an operand and an index at the same time. +pragma[noinline] +predicate nodeHasOperand(Node node, Operand operand, int indirectionIndex) { + node.asOperand() = operand and indirectionIndex = 0 + or + hasOperandAndIndex(node, operand, indirectionIndex) +} + +// Needed to join on both an instruction and an index at the same time. +pragma[noinline] +predicate nodeHasInstruction(Node node, Instruction instr, int indirectionIndex) { + node.asInstruction() = instr and indirectionIndex = 0 + or + hasInstructionAndIndex(node, instr, indirectionIndex) +} + +/** + * Holds if data can flow from `node1` to `node2` via a read of `f`. + * Thus, `node1` references an object with a field `f` whose value ends up in + * `node2`. + */ +predicate readStep(Node node1, Content c, Node node2) { + exists(FieldAddress fa1, Operand operand, int numberOfLoads, int indirectionIndex2 | + nodeHasOperand(node2, operand, indirectionIndex2) and + nodeHasOperand(node1, fa1.getObjectAddressOperand(), _) and + numberOfLoadsFromOperand(fa1, operand, numberOfLoads) + | + exists(FieldContent fc | fc = c | + fc.getField() = fa1.getField() and + fc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads + ) + or + exists(UnionContent uc | uc = c | + uc.getAField() = fa1.getField() and + uc.getIndirectionIndex() = indirectionIndex2 + numberOfLoads + ) + ) +} + +/** + * Holds if values stored inside content `c` are cleared at node `n`. + */ +predicate clearsContent(Node n, Content c) { + none() // stub implementation +} + +/** + * Holds if the value that is being tracked is expected to be stored inside content `c` + * at node `n`. + */ +predicate expectsContent(Node n, ContentSet c) { none() } + +/** Gets the type of `n` used for type pruning. */ +IRType getNodeType(Node n) { + suppressUnusedNode(n) and + result instanceof IRVoidType // stub implementation +} + +/** Gets a string representation of a type returned by `getNodeType`. */ +string ppReprType(IRType t) { none() } // stub implementation + +/** + * Holds if `t1` and `t2` are compatible, that is, whether data can flow from + * a node of type `t1` to a node of type `t2`. + */ +pragma[inline] +predicate compatibleTypes(IRType t1, IRType t2) { + any() // stub implementation +} + +private predicate suppressUnusedNode(Node n) { any() } + +////////////////////////////////////////////////////////////////////////////// +// Java QL library compatibility wrappers +////////////////////////////////////////////////////////////////////////////// +/** A node that performs a type cast. */ +class CastNode extends Node { + CastNode() { none() } // stub implementation +} + +/** + * A function that may contain code or a variable that may contain itself. When + * flow crosses from one _enclosing callable_ to another, the interprocedural + * data-flow library discards call contexts and inserts a node in the big-step + * relation used for human-readable path explanations. + */ +class DataFlowCallable = Cpp::Declaration; + +class DataFlowExpr = Expr; + +class DataFlowType = IRType; + +/** A function call relevant for data flow. */ +class DataFlowCall extends CallInstruction { + Function getEnclosingCallable() { result = this.getEnclosingFunction() } +} + +predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation + +int accessPathLimit() { result = 5 } + +/** + * Holds if access paths with `c` at their head always should be tracked at high + * precision. This disables adaptive access path precision for such access paths. + */ +predicate forceHighPrecision(Content c) { none() } + +/** The unit type. */ +private newtype TUnit = TMkUnit() + +/** The trivial type with a single element. */ +class Unit extends TUnit { + /** Gets a textual representation of this element. */ + string toString() { result = "unit" } +} + +/** Holds if `n` should be hidden from path explanations. */ +predicate nodeIsHidden(Node n) { n instanceof OperandNode and not n instanceof ArgumentNode } + +class LambdaCallKind = Unit; + +/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ +predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { none() } + +/** Holds if `call` is a lambda call of kind `kind` where `receiver` is the lambda expression. */ +predicate lambdaCall(DataFlowCall call, LambdaCallKind kind, Node receiver) { none() } + +/** Extra data-flow steps needed for lambda flow analysis. */ +predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preservesValue) { none() } + +/** + * Holds if flow is allowed to pass from parameter `p` and back to itself as a + * side-effect, resulting in a summary from `p` to itself. + * + * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed + * by default as a heuristic. + */ +predicate allowParameterReturnInSelf(ParameterNode p) { none() } + +private class MyConsistencyConfiguration extends Consistency::ConsistencyConfiguration { + override predicate argHasPostUpdateExclude(ArgumentNode n) { + // The rules for whether an IR argument gets a post-update node are too + // complex to model here. + any() + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll new file mode 100644 index 00000000000..b3b055bc9a1 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -0,0 +1,1346 @@ +/** + * Provides C++-specific definitions for use in the data flow library. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.controlflow.IRGuards +private import semmle.code.cpp.models.interfaces.DataFlow +private import DataFlowPrivate +private import ModelUtil +private import SsaInternals as Ssa + +cached +private module Cached { + /** + * The IR dataflow graph consists of the following nodes: + * - `InstructionNode`, which injects most instructions directly into the dataflow graph. + * - `OperandNode`, which similarly injects most operands directly into the dataflow graph. + * - `VariableNode`, which is used to model flow through global variables. + * - `PostFieldUpdateNode`, which is used to model the state of a field after a value has been stored + * into an address after a number of loads. + * - `SsaPhiNode`, which represents phi nodes as computed by the shared SSA library. + * - `IndirectArgumentOutNode`, which represents the value of an argument (and its indirections) after + * it leaves a function call. + * - `IndirectOperand`, which represents the value of `operand` after loading the address a number + * of times. + * - `IndirectInstruction`, which represents the value of `instr` after loading the address a number + * of times. + */ + cached + newtype TIRDataFlowNode = + TInstructionNode(Instruction i) { not Ssa::ignoreInstruction(i) } or + TOperandNode(Operand op) { not Ssa::ignoreOperand(op) } or + TVariableNode(Variable var) or + TPostFieldUpdateNode(FieldAddress operand, int indirectionIndex) { + indirectionIndex = + [0 .. Ssa::countIndirectionsForCppType(operand.getObjectAddress().getResultLanguageType()) - + 1] + } or + TSsaPhiNode(Ssa::PhiNode phi) or + TIndirectArgumentOutNode(ArgumentOperand operand, int indirectionIndex) { + Ssa::isModifiableByCall(operand) and + indirectionIndex = [0 .. Ssa::countIndirectionsForCppType(operand.getLanguageType()) - 1] + } or + TIndirectOperand(Operand op, int indirectionIndex) { + Ssa::hasIndirectOperand(op, indirectionIndex) + } or + TIndirectInstruction(Instruction instr, int indirectionIndex) { + Ssa::hasIndirectInstruction(instr, indirectionIndex) + } +} + +/** + * An operand that is defined by a `FieldAddressInstruction`. + */ +class FieldAddress extends Operand { + FieldAddressInstruction fai; + + FieldAddress() { fai = this.getDef() } + + /** Gets the field associated with this instruction. */ + Field getField() { result = fai.getField() } + + /** Gets the instruction whose result provides the address of the object containing the field. */ + Instruction getObjectAddress() { result = fai.getObjectAddress() } + + /** Gets the operand that provides the address of the object containing the field. */ + Operand getObjectAddressOperand() { result = fai.getObjectAddressOperand() } +} + +/** + * Holds if `opFrom` is an operand whose value flows to the result of `instrTo`. + * + * `isPointerArith` is `true` if `instrTo` is a `PointerArithmeticInstruction` and `opFrom` + * is the left operand. + */ +predicate conversionFlow(Operand opFrom, Instruction instrTo, boolean isPointerArith) { + isPointerArith = false and + ( + instrTo.(CopyValueInstruction).getSourceValueOperand() = opFrom + or + instrTo.(ConvertInstruction).getUnaryOperand() = opFrom + or + instrTo.(CheckedConvertOrNullInstruction).getUnaryOperand() = opFrom + or + instrTo.(InheritanceConversionInstruction).getUnaryOperand() = opFrom + ) + or + isPointerArith = true and + instrTo.(PointerArithmeticInstruction).getLeftOperand() = opFrom +} + +private import Cached + +/** + * A node in a data flow graph. + * + * A node can be either an expression, a parameter, or an uninitialized local + * variable. Such nodes are created with `DataFlow::exprNode`, + * `DataFlow::parameterNode`, and `DataFlow::uninitializedNode` respectively. + */ +class Node extends TIRDataFlowNode { + /** + * INTERNAL: Do not use. + */ + Declaration getEnclosingCallable() { none() } // overridden in subclasses + + /** Gets the function to which this node belongs, if any. */ + Declaration getFunction() { none() } // overridden in subclasses + + /** Gets the type of this node. */ + IRType getType() { none() } // overridden in subclasses + + /** Gets the instruction corresponding to this node, if any. */ + Instruction asInstruction() { result = this.(InstructionNode).getInstruction() } + + /** Gets the operands corresponding to this node, if any. */ + Operand asOperand() { result = this.(OperandNode).getOperand() } + + /** + * Gets the non-conversion expression corresponding to this node, if any. + * This predicate only has a result on nodes that represent the value of + * evaluating the expression. For data flowing _out of_ an expression, like + * when an argument is passed by reference, use `asDefiningArgument` instead + * of `asExpr`. + * + * If this node strictly (in the sense of `asConvertedExpr`) corresponds to + * a `Conversion`, then the result is the underlying non-`Conversion` base + * expression. + */ + Expr asExpr() { result = this.(ExprNode).getExpr() } + + /** + * Gets the non-conversion expression that's indirectly tracked by this node + * under `index` number of indirections. + */ + Expr asIndirectExpr(int index) { + exists(Operand operand | hasOperandAndIndex(this, operand, index) | + result = operand.getDef().getUnconvertedResultExpression() + ) + } + + /** + * Gets the non-conversion expression that's indirectly tracked by this node + * behind a number of indirections. + */ + Expr asIndirectExpr() { result = this.asIndirectExpr(_) } + + /** + * Gets the expression corresponding to this node, if any. The returned + * expression may be a `Conversion`. + */ + Expr asConvertedExpr() { result = this.(ExprNode).getConvertedExpr() } + + /** + * Gets the expression that's indirectly tracked by this node + * behind `index` number of indirections. + */ + Expr asIndirectConvertedExpr(int index) { + exists(Operand operand | hasOperandAndIndex(this, operand, index) | + result = operand.getDef().getConvertedResultExpression() + ) + } + + /** + * Gets the expression that's indirectly tracked by this node behind a + * number of indirections. + */ + Expr asIndirectConvertedExpr() { result = this.asIndirectConvertedExpr(_) } + + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * This predicate should be used instead of `asExpr` when referring to the + * value of a reference argument _after_ the call has returned. For example, + * in `f(&x)`, this predicate will have `&x` as its result for the `Node` + * that represents the new value of `x`. + */ + Expr asDefiningArgument() { result = this.asDefiningArgument(_) } + + /** + * Gets the argument that defines this `DefinitionByReferenceNode`, if any. + * + * Unlike `Node::asDefiningArgument/0`, this predicate gets the node representing + * the value of the `index`'th indirection after leaving a function. For example, + * in: + * ```cpp + * void f(int**); + * ... + * int** x = ...; + * f(x); + * ``` + * The node `n` such that `n.asDefiningArgument(1)` is the argument `x` will + * contain the value of `*x` after `f` has returned, and the node `n` such that + * `n.asDefiningArgument(2)` is the argument `x` will contain the value of `**x` + * after the `f` has returned. + */ + Expr asDefiningArgument(int index) { + // Subtract one because `DefinitionByReferenceNode` is defined to be in + // the range `[0 ... n - 1]` for some `n` instead of `[1 ... n]`. + this.(DefinitionByReferenceNode).getIndirectionIndex() = index - 1 and + result = this.(DefinitionByReferenceNode).getArgument() + } + + /** + * Gets the the argument going into a function for a node that represents + * the indirect value of the argument after `index` loads. For example, in: + * ```cpp + * void f(int**); + * ... + * int** x = ...; + * f(x); + * ``` + * The node `n` such that `n.asIndirectArgument(1)` represents the value of + * `*x` going into `f`, and the node `n` such that `n.asIndirectArgument(2)` + * represents the value of `**x` going into `f`. + */ + Expr asIndirectArgument(int index) { + this.(SideEffectOperandNode).getIndirectionIndex() = index and + result = this.(SideEffectOperandNode).getArgument() + } + + /** + * Gets the the argument going into a function for a node that represents + * the indirect value of the argument after any non-zero number of loads. + */ + Expr asIndirectArgument() { result = this.asIndirectArgument(_) } + + /** Gets the positional parameter corresponding to this node, if any. */ + Parameter asParameter() { result = asParameter(0) } + + /** + * Gets the positional parameter corresponding to the node that represents + * the value of the parameter after `index` number of loads, if any. For + * example, in: + * ```cpp + * void f(int** x) { ... } + * ``` + * - The node `n` such that `n.asParameter(0)` is the parameter `x` represents + * the value of `x`. + * - The node `n` such that `n.asParameter(1)` is the parameter `x` represents + * the value of `*x`. + * The node `n` such that `n.asParameter(2)` is the parameter `x` represents + * the value of `**x`. + */ + Parameter asParameter(int index) { + index = 0 and + result = this.(ExplicitParameterNode).getParameter() + or + this.(IndirectParameterNode).getIndirectionIndex() = index and + result = this.(IndirectParameterNode).getParameter() + } + + /** + * Gets the variable corresponding to this node, if any. This can be used for + * modeling flow in and out of global variables. + */ + Variable asVariable() { result = this.(VariableNode).getVariable() } + + /** + * Gets the expression that is partially defined by this node, if any. + * + * Partial definitions are created for field stores (`x.y = taint();` is a partial + * definition of `x`), and for calls that may change the value of an object (so + * `x.set(taint())` is a partial definition of `x`, and `transfer(&x, taint())` is + * a partial definition of `&x`). + */ + Expr asPartialDefinition() { result = this.(PartialDefinitionNode).getDefinedExpr() } + + /** + * Gets an upper bound on the type of this node. + */ + IRType getTypeBound() { result = this.getType() } + + /** Gets the location of this element. */ + cached + final Location getLocation() { result = this.getLocationImpl() } + + /** INTERNAL: Do not use. */ + Location getLocationImpl() { + none() // overridden by subclasses + } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo( + string filepath, int startline, int startcolumn, int endline, int endcolumn + ) { + this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) + } + + /** Gets a textual representation of this element. */ + cached + final string toString() { result = this.toStringImpl() } + + /** INTERNAL: Do not use. */ + string toStringImpl() { + none() // overridden by subclasses + } +} + +/** + * An instruction, viewed as a node in a data flow graph. + */ +class InstructionNode extends Node, TInstructionNode { + Instruction instr; + + InstructionNode() { this = TInstructionNode(instr) } + + /** Gets the instruction corresponding to this node. */ + Instruction getInstruction() { result = instr } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Declaration getFunction() { result = instr.getEnclosingFunction() } + + override IRType getType() { result = instr.getResultIRType() } + + final override Location getLocationImpl() { result = instr.getLocation() } + + override string toStringImpl() { + // This predicate is overridden in subclasses. This default implementation + // does not use `Instruction.toString` because that's expensive to compute. + result = this.getInstruction().getOpcode().toString() + } +} + +/** + * An operand, viewed as a node in a data flow graph. + */ +class OperandNode extends Node, TOperandNode { + Operand op; + + OperandNode() { this = TOperandNode(op) } + + /** Gets the operand corresponding to this node. */ + Operand getOperand() { result = op } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Declaration getFunction() { result = op.getUse().getEnclosingFunction() } + + override IRType getType() { result = op.getIRType() } + + final override Location getLocationImpl() { result = op.getLocation() } + + override string toStringImpl() { result = this.getOperand().toString() } +} + +/** + * INTERNAL: do not use. + * + * The node representing the value of a field after it has been updated. + */ +class PostFieldUpdateNode extends TPostFieldUpdateNode, PartialDefinitionNode { + int indirectionIndex; + FieldAddress fieldAddress; + + PostFieldUpdateNode() { this = TPostFieldUpdateNode(fieldAddress, indirectionIndex) } + + override Function getFunction() { result = fieldAddress.getUse().getEnclosingFunction() } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override IRType getType() { result = fieldAddress.getIRType() } + + FieldAddress getFieldAddress() { result = fieldAddress } + + Field getUpdatedField() { result = fieldAddress.getField() } + + int getIndirectionIndex() { result = indirectionIndex } + + override Node getPreUpdateNode() { + // + 1 because we're storing into an lvalue, and the original node should be the rvalue of + // the same address. + hasOperandAndIndex(result, pragma[only_bind_into](fieldAddress).getObjectAddressOperand(), + indirectionIndex + 1) + } + + override Expr getDefinedExpr() { + result = fieldAddress.getObjectAddress().getUnconvertedResultExpression() + } + + override Location getLocationImpl() { result = fieldAddress.getLocation() } + + override string toStringImpl() { result = this.getPreUpdateNode() + " [post update]" } +} + +/** + * INTERNAL: do not use. + * + * A phi node produced by the shared SSA library, viewed as a node in a data flow graph. + */ +class SsaPhiNode extends Node, TSsaPhiNode { + Ssa::PhiNode phi; + + SsaPhiNode() { this = TSsaPhiNode(phi) } + + /** Gets the phi node associated with this node. */ + Ssa::PhiNode getPhiNode() { result = phi } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Declaration getFunction() { result = phi.getBasicBlock().getEnclosingFunction() } + + override IRType getType() { result instanceof IRVoidType } + + final override Location getLocationImpl() { result = phi.getBasicBlock().getLocation() } + + override string toStringImpl() { result = "Phi" } +} + +/** + * INTERNAL: do not use. + * + * A node representing a value after leaving a function. + */ +class SideEffectOperandNode extends Node, IndirectOperand { + CallInstruction call; + int argumentIndex; + + SideEffectOperandNode() { operand = call.getArgumentOperand(argumentIndex) } + + CallInstruction getCallInstruction() { result = call } + + Operand getAddressOperand() { result = operand } + + int getArgumentIndex() { result = argumentIndex } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Function getFunction() { result = call.getEnclosingFunction() } + + override IRType getType() { result instanceof IRVoidType } + + Expr getArgument() { result = call.getArgument(argumentIndex).getUnconvertedResultExpression() } +} + +/** + * INTERNAL: do not use. + * + * A node representing an indirection of a parameter. + */ +class IndirectParameterNode extends Node, IndirectInstruction { + InitializeParameterInstruction init; + + IndirectParameterNode() { this.getInstruction() = init } + + int getArgumentIndex() { init.hasIndex(result) } + + /** Gets the parameter whose indirection is initialized. */ + Parameter getParameter() { result = init.getParameter() } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Function getFunction() { result = this.getInstruction().getEnclosingFunction() } + + override IRType getType() { result instanceof IRVoidType } + + override string toStringImpl() { + result = this.getParameter().toString() + " indirection" + or + not exists(this.getParameter()) and + result = "this indirection" + } +} + +/** + * INTERNAL: do not use. + * + * A node representing the indirection of a value that is + * about to be returned from a function. + */ +class IndirectReturnNode extends IndirectOperand { + IndirectReturnNode() { + this.getOperand() = any(ReturnIndirectionInstruction ret).getSourceAddressOperand() + or + this.getOperand() = any(ReturnValueInstruction ret).getReturnAddressOperand() + } + + Operand getAddressOperand() { result = operand } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override IRType getType() { result instanceof IRVoidType } +} + +/** + * INTERNAL: do not use. + * + * A node representing the indirection of a value after it + * has been returned from a function. + */ +class IndirectArgumentOutNode extends Node, TIndirectArgumentOutNode, PostUpdateNode { + ArgumentOperand operand; + int indirectionIndex; + + IndirectArgumentOutNode() { this = TIndirectArgumentOutNode(operand, indirectionIndex) } + + int getIndirectionIndex() { result = indirectionIndex } + + int getArgumentIndex() { + exists(CallInstruction call | call.getArgumentOperand(result) = operand) + } + + Operand getAddressOperand() { result = operand } + + CallInstruction getCallInstruction() { result.getAnArgumentOperand() = operand } + + Function getStaticCallTarget() { result = this.getCallInstruction().getStaticCallTarget() } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override Function getFunction() { result = this.getCallInstruction().getEnclosingFunction() } + + override IRType getType() { result instanceof IRVoidType } + + override Node getPreUpdateNode() { hasOperandAndIndex(result, operand, indirectionIndex + 1) } + + override string toStringImpl() { + // This string should be unique enough to be helpful but common enough to + // avoid storing too many different strings. + result = this.getStaticCallTarget().getName() + " output argument" + or + not exists(this.getStaticCallTarget()) and + result = "output argument" + } + + override Location getLocationImpl() { result = operand.getLocation() } +} + +pragma[nomagic] +predicate indirectReturnOutNodeOperand0(CallInstruction call, Operand operand, int indirectionIndex) { + Ssa::hasIndirectInstruction(call, indirectionIndex) and + operandForfullyConvertedCall(operand, call) +} + +pragma[nomagic] +predicate indirectReturnOutNodeInstruction0( + CallInstruction call, Instruction instr, int indirectionIndex +) { + Ssa::hasIndirectInstruction(call, indirectionIndex) and + instructionForfullyConvertedCall(instr, call) +} + +/** + * INTERNAL: do not use. + * + * A node representing the value of a function call. + */ +class IndirectReturnOutNode extends Node { + CallInstruction call; + int indirectionIndex; + + IndirectReturnOutNode() { + // Annoyingly, we need to pick the fully converted value as the output of the function to + // make flow through in the shared dataflow library work correctly. + exists(Operand operand | + indirectReturnOutNodeOperand0(call, operand, indirectionIndex) and + hasOperandAndIndex(this, operand, indirectionIndex) + ) + or + exists(Instruction instr | + indirectReturnOutNodeInstruction0(call, instr, indirectionIndex) and + hasInstructionAndIndex(this, instr, indirectionIndex) + ) + } + + CallInstruction getCallInstruction() { result = call } + + int getIndirectionIndex() { result = indirectionIndex } +} + +/** + * INTERNAL: Do not use. + * + * A node that represents the indirect value of an operand in the IR + * after `index` number of loads. + */ +class IndirectOperand extends Node, TIndirectOperand { + Operand operand; + int indirectionIndex; + + IndirectOperand() { this = TIndirectOperand(operand, indirectionIndex) } + + /** Gets the underlying instruction. */ + Operand getOperand() { result = operand } + + int getIndirectionIndex() { result = indirectionIndex } + + override Function getFunction() { result = this.getOperand().getDef().getEnclosingFunction() } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override IRType getType() { result = this.getOperand().getIRType() } + + final override Location getLocationImpl() { result = this.getOperand().getLocation() } + + override string toStringImpl() { + result = instructionNode(this.getOperand().getDef()).toStringImpl() + " indirection" + } +} + +/** + * INTERNAL: Do not use. + * + * A node that represents the indirect value of an instruction in the IR + * after `index` number of loads. + */ +class IndirectInstruction extends Node, TIndirectInstruction { + Instruction instr; + int indirectionIndex; + + IndirectInstruction() { this = TIndirectInstruction(instr, indirectionIndex) } + + /** Gets the underlying instruction. */ + Instruction getInstruction() { result = instr } + + int getIndirectionIndex() { result = indirectionIndex } + + override Function getFunction() { result = this.getInstruction().getEnclosingFunction() } + + override Declaration getEnclosingCallable() { result = this.getFunction() } + + override IRType getType() { result = this.getInstruction().getResultIRType() } + + final override Location getLocationImpl() { result = this.getInstruction().getLocation() } + + override string toStringImpl() { + result = instructionNode(this.getInstruction()).toStringImpl() + " indirection" + } +} + +private predicate isFullyConvertedArgument(Expr e) { + e = any(Call call).getAnArgument().getFullyConverted() +} + +private predicate isFullyConvertedCall(Expr e) { e = any(Call call).getFullyConverted() } + +/** Holds if `Node::asExpr` should map an some operand node to `e`. */ +private predicate convertedExprMustBeOperand(Expr e) { + isFullyConvertedArgument(e) + or + isFullyConvertedCall(e) +} + +/** Holds if `node` is an `OperandNode` that should map `node.asExpr()` to `e`. */ +predicate exprNodeShouldBeOperand(Node node, Expr e) { + e = node.asOperand().getDef().getConvertedResultExpression() and + convertedExprMustBeOperand(e) +} + +/** + * Holds if `load` is a `LoadInstruction` that is the result of evaluating `e` + * and `node` is an `IndirctOperandNode` that should map `node.asExpr()` to `e`. + * + * We map `e` to `node.asExpr()` when `node` semantically represents the + * same value as `load`. A subsequent flow step will flow `node` to + * the `LoadInstruction`. + */ +private predicate exprNodeShouldBeIndirectOperand(IndirectOperand node, Expr e, LoadInstruction load) { + node.getIndirectionIndex() = 1 and + e = load.getConvertedResultExpression() and + load.getSourceAddressOperand() = node.getOperand() and + not convertedExprMustBeOperand(e) +} + +/** Holds if `node` should be an instruction node that maps `node.asExpr()` to `e`. */ +predicate exprNodeShouldBeInstruction(Node node, Expr e) { + e = node.asInstruction().getConvertedResultExpression() and + not exprNodeShouldBeOperand(_, e) and + not exprNodeShouldBeIndirectOperand(_, e, _) +} + +private class ExprNodeBase extends Node { + Expr getConvertedExpr() { none() } // overridden by subclasses + + Expr getExpr() { none() } // overridden by subclasses +} + +private class InstructionExprNode extends ExprNodeBase, InstructionNode { + InstructionExprNode() { exprNodeShouldBeInstruction(this, _) } + + final override Expr getConvertedExpr() { exprNodeShouldBeInstruction(this, result) } + + final override Expr getExpr() { result = this.getInstruction().getUnconvertedResultExpression() } + + final override string toStringImpl() { result = this.getConvertedExpr().toString() } +} + +private class OperandExprNode extends ExprNodeBase, OperandNode { + OperandExprNode() { exprNodeShouldBeOperand(this, _) } + + final override Expr getConvertedExpr() { exprNodeShouldBeOperand(this, result) } + + final override Expr getExpr() { + result = this.getOperand().getDef().getUnconvertedResultExpression() + } + + final override string toStringImpl() { + // Avoid generating multiple `toString` results for `ArgumentNode`s + // since they have a better `toString`. + result = this.(ArgumentNode).toStringImpl() + or + not this instanceof ArgumentNode and + result = this.getConvertedExpr().toString() + } +} + +private class IndirectOperandExprNode extends ExprNodeBase, IndirectOperand { + LoadInstruction load; + + IndirectOperandExprNode() { exprNodeShouldBeIndirectOperand(this, _, load) } + + final override Expr getConvertedExpr() { exprNodeShouldBeIndirectOperand(this, result, load) } + + final override Expr getExpr() { result = load.getUnconvertedResultExpression() } + + final override string toStringImpl() { result = this.getConvertedExpr().toString() } +} + +/** + * An expression, viewed as a node in a data flow graph. + */ +class ExprNode extends Node instanceof ExprNodeBase { + /** + * Gets the non-conversion expression corresponding to this node, if any. If + * this node strictly (in the sense of `getConvertedExpr`) corresponds to a + * `Conversion`, then the result is that `Conversion`'s non-`Conversion` base + * expression. + */ + Expr getExpr() { result = super.getExpr() } + + /** + * Gets the expression corresponding to this node, if any. The returned + * expression may be a `Conversion`. + */ + Expr getConvertedExpr() { result = super.getConvertedExpr() } +} + +/** + * The value of a parameter at function entry, viewed as a node in a data + * flow graph. This includes both explicit parameters such as `x` in `f(x)` + * and implicit parameters such as `this` in `x.f()`. + * + * To match a specific kind of parameter, consider using one of the subclasses + * `ExplicitParameterNode`, `ThisParameterNode`, or + * `ParameterIndirectionNode`. + */ +class ParameterNode extends Node { + ParameterNode() { + // To avoid making this class abstract, we enumerate its values here + this.asInstruction() instanceof InitializeParameterInstruction + or + this instanceof IndirectParameterNode + } + + /** + * Holds if this node is the parameter of `f` at the specified position. The + * implicit `this` parameter is considered to have position `-1`, and + * pointer-indirection parameters are at further negative positions. + */ + predicate isParameterOf(Function f, ParameterPosition pos) { none() } // overridden by subclasses +} + +/** An explicit positional parameter, not including `this` or `...`. */ +private class ExplicitParameterNode extends ParameterNode, InstructionNode { + override InitializeParameterInstruction instr; + + ExplicitParameterNode() { exists(instr.getParameter()) } + + override predicate isParameterOf(Function f, ParameterPosition pos) { + f.getParameter(pos.(DirectPosition).getIndex()) = instr.getParameter() + } + + /** Gets the `Parameter` associated with this node. */ + Parameter getParameter() { result = instr.getParameter() } + + override string toStringImpl() { result = instr.getParameter().toString() } +} + +/** An implicit `this` parameter. */ +class ThisParameterNode extends ParameterNode, InstructionNode { + override InitializeParameterInstruction instr; + + ThisParameterNode() { instr.getIRVariable() instanceof IRThisVariable } + + override predicate isParameterOf(Function f, ParameterPosition pos) { + pos.(DirectPosition).getIndex() = -1 and instr.getEnclosingFunction() = f + } + + override string toStringImpl() { result = "this" } +} + +pragma[noinline] +private predicate indirectPostionHasArgumentIndexAndIndex( + IndirectionPosition pos, int argumentIndex, int indirectionIndex +) { + pos.getArgumentIndex() = argumentIndex and + pos.getIndirectionIndex() = indirectionIndex +} + +pragma[noinline] +private predicate indirectParameterNodeHasArgumentIndexAndIndex( + IndirectParameterNode node, int argumentIndex, int indirectionIndex +) { + node.getArgumentIndex() = argumentIndex and + node.getIndirectionIndex() = indirectionIndex +} + +/** A synthetic parameter to model the pointed-to object of a pointer parameter. */ +class ParameterIndirectionNode extends ParameterNode instanceof IndirectParameterNode { + override predicate isParameterOf(Function f, ParameterPosition pos) { + IndirectParameterNode.super.getEnclosingCallable() = f and + exists(int argumentIndex, int indirectionIndex | + indirectPostionHasArgumentIndexAndIndex(pos, argumentIndex, indirectionIndex) and + indirectParameterNodeHasArgumentIndexAndIndex(this, argumentIndex, indirectionIndex) + ) + } +} + +/** + * A node associated with an object after an operation that might have + * changed its state. + * + * This can be either the argument to a callable after the callable returns + * (which might have mutated the argument), or the qualifier of a field after + * an update to the field. + * + * Nodes corresponding to AST elements, for example `ExprNode`, usually refer + * to the value before the update with the exception of `ClassInstanceExpr`, + * which represents the value after the constructor has run. + */ +abstract class PostUpdateNode extends Node { + /** + * Gets the node before the state update. + */ + abstract Node getPreUpdateNode(); +} + +/** + * The base class for nodes that perform "partial definitions". + * + * In contrast to a normal "definition", which provides a new value for + * something, a partial definition is an expression that may affect a + * value, but does not necessarily replace it entirely. For example: + * ``` + * x.y = 1; // a partial definition of the object `x`. + * x.y.z = 1; // a partial definition of the object `x.y` and `x`. + * x.setY(1); // a partial definition of the object `x`. + * setY(&x); // a partial definition of the object `x`. + * ``` + */ +abstract private class PartialDefinitionNode extends PostUpdateNode { + abstract Expr getDefinedExpr(); +} + +/** + * A node that represents the value of a variable after a function call that + * may have changed the variable because it's passed by reference. + * + * A typical example would be a call `f(&x)`. Firstly, there will be flow into + * `x` from previous definitions of `x`. Secondly, there will be a + * `DefinitionByReferenceNode` to represent the value of `x` after the call has + * returned. This node will have its `getArgument()` equal to `&x` and its + * `getVariableAccess()` equal to `x`. + */ +class DefinitionByReferenceNode extends IndirectArgumentOutNode { + /** Gets the unconverted argument corresponding to this node. */ + Expr getArgument() { result = this.getAddressOperand().getDef().getUnconvertedResultExpression() } + + /** Gets the parameter through which this value is assigned. */ + Parameter getParameter() { + result = this.getCallInstruction().getStaticCallTarget().getParameter(this.getArgumentIndex()) + } +} + +/** + * A `Node` corresponding to a variable in the program, as opposed to the + * value of that variable at some particular point. This can be used for + * modeling flow in and out of global variables. + */ +class VariableNode extends Node, TVariableNode { + Variable v; + + VariableNode() { this = TVariableNode(v) } + + /** Gets the variable corresponding to this node. */ + Variable getVariable() { result = v } + + override Declaration getFunction() { none() } + + override Declaration getEnclosingCallable() { + // When flow crosses from one _enclosing callable_ to another, the + // interprocedural data-flow library discards call contexts and inserts a + // node in the big-step relation used for human-readable path explanations. + // Therefore we want a distinct enclosing callable for each `VariableNode`, + // and that can be the `Variable` itself. + result = v + } + + override IRType getType() { result.getCanonicalLanguageType().hasUnspecifiedType(v.getType(), _) } + + final override Location getLocationImpl() { result = v.getLocation() } + + override string toStringImpl() { result = v.toString() } +} + +/** + * Gets the node corresponding to `instr`. + */ +InstructionNode instructionNode(Instruction instr) { result.getInstruction() = instr } + +/** + * Gets the node corresponding to `operand`. + */ +OperandNode operandNode(Operand operand) { result.getOperand() = operand } + +/** + * Gets the `Node` corresponding to the value of evaluating `e` or any of its + * conversions. There is no result if `e` is a `Conversion`. For data flowing + * _out of_ an expression, like when an argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. + */ +ExprNode exprNode(Expr e) { result.getExpr() = e } + +/** + * Gets the `Node` corresponding to the value of evaluating `e`. Here, `e` may + * be a `Conversion`. For data flowing _out of_ an expression, like when an + * argument is passed by reference, use + * `definitionByReferenceNodeFromArgument` instead. + */ +ExprNode convertedExprNode(Expr e) { result.getConvertedExpr() = e } + +/** + * Gets the `Node` corresponding to the value of `p` at function entry. + */ +ExplicitParameterNode parameterNode(Parameter p) { result.getParameter() = p } + +/** + * Gets the `Node` corresponding to a definition by reference of the variable + * that is passed as unconverted `argument` of a call. + */ +DefinitionByReferenceNode definitionByReferenceNodeFromArgument(Expr argument) { + result.getArgument() = argument +} + +/** Gets the `VariableNode` corresponding to the variable `v`. */ +VariableNode variableNode(Variable v) { result.getVariable() = v } + +/** + * DEPRECATED: See UninitializedNode. + * + * Gets the `Node` corresponding to the value of an uninitialized local + * variable `v`. + */ +Node uninitializedNode(LocalVariable v) { none() } + +/** + * Holds if data flows from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localFlowStep = simpleLocalFlowStep/2; + +private predicate indirectionOperandFlow(IndirectOperand nodeFrom, Node nodeTo) { + // Reduce the indirection count by 1 if we're passing through a `LoadInstruction`. + exists(int ind, LoadInstruction load | + hasOperandAndIndex(nodeFrom, load.getSourceAddressOperand(), ind) and + nodeHasInstruction(nodeTo, load, ind - 1) + ) + or + // If an operand flows to an instruction, then the indirection of + // the operand also flows to the indirction of the instruction. + exists(Operand operand, Instruction instr, int indirectionIndex | + simpleInstructionLocalFlowStep(operand, instr) and + hasOperandAndIndex(nodeFrom, operand, indirectionIndex) and + hasInstructionAndIndex(nodeTo, instr, indirectionIndex) + ) + or + // If there's indirect flow to an operand, then there's also indirect + // flow to the operand after applying some pointer arithmetic. + exists(PointerArithmeticInstruction pointerArith, int indirectionIndex | + hasOperandAndIndex(nodeFrom, pointerArith.getAnOperand(), indirectionIndex) and + hasInstructionAndIndex(nodeTo, pointerArith, indirectionIndex) + ) +} + +pragma[noinline] +predicate hasOperandAndIndex(IndirectOperand indirectOperand, Operand operand, int indirectionIndex) { + indirectOperand.getOperand() = operand and + indirectOperand.getIndirectionIndex() = indirectionIndex +} + +pragma[noinline] +predicate hasInstructionAndIndex( + IndirectInstruction indirectInstr, Instruction instr, int indirectionIndex +) { + indirectInstr.getInstruction() = instr and + indirectInstr.getIndirectionIndex() = indirectionIndex +} + +private predicate indirectionInstructionFlow(IndirectInstruction nodeFrom, IndirectOperand nodeTo) { + // If there's flow from an instruction to an operand, then there's also flow from the + // indirect instruction to the indirect operand. + exists(Operand operand, Instruction instr, int indirectionIndex | + simpleOperandLocalFlowStep(pragma[only_bind_into](instr), pragma[only_bind_into](operand)) + | + hasOperandAndIndex(nodeTo, operand, indirectionIndex) and + hasInstructionAndIndex(nodeFrom, instr, indirectionIndex) + ) +} + +/** + * INTERNAL: do not use. + * + * This is the local flow predicate that's used as a building block in global + * data flow. It may have less flow than the `localFlowStep` predicate. + */ +predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + // Post update node -> Node flow + Ssa::ssaFlow(nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) + or + // Def-use/Use-use flow + Ssa::ssaFlow(nodeFrom, nodeTo) + or + // Operand -> Instruction flow + simpleInstructionLocalFlowStep(nodeFrom.asOperand(), nodeTo.asInstruction()) + or + // Instruction -> Operand flow + simpleOperandLocalFlowStep(nodeFrom.asInstruction(), nodeTo.asOperand()) + or + // Phi node -> Node flow + Ssa::fromPhiNode(nodeFrom, nodeTo) + or + // Indirect operand -> (indirect) instruction flow + indirectionOperandFlow(nodeFrom, nodeTo) + or + // Indirect instruction -> indirect operand flow + indirectionInstructionFlow(nodeFrom, nodeTo) + or + // Flow through modeled functions + modelFlow(nodeFrom, nodeTo) + or + // Reverse flow: data that flows from the definition node back into the indirection returned + // by a function. This allows data to flow 'in' through references returned by a modeled + // function such as `operator[]`. + exists(Operand address, int indirectionIndex | + nodeHasOperand(nodeTo.(IndirectReturnOutNode), address, indirectionIndex) + | + exists(StoreInstruction store | + nodeHasInstruction(nodeFrom, store, indirectionIndex - 1) and + store.getDestinationAddressOperand() = address + ) + or + Ssa::outNodeHasAddressAndIndex(nodeFrom, address, indirectionIndex - 1) + ) +} + +pragma[noinline] +private predicate getAddressType(LoadInstruction load, Type t) { + exists(Instruction address | + address = load.getSourceAddress() and + t = address.getResultType() + ) +} + +/** + * Like the AST dataflow library, we want to conflate the address and value of a reference. This class + * represents the `LoadInstruction` that is generated from a reference dereference. + */ +private class ReferenceDereferenceInstruction extends LoadInstruction { + ReferenceDereferenceInstruction() { + exists(ReferenceType ref | + getAddressType(this, ref) and + this.getResultType() = ref.getBaseType() + ) + } +} + +private predicate simpleInstructionLocalFlowStep(Operand opFrom, Instruction iTo) { + // Treat all conversions as flow, even conversions between different numeric types. + conversionFlow(opFrom, iTo, false) + or + iTo.(CopyInstruction).getSourceValueOperand() = opFrom + or + // Conflate references and values like in AST dataflow. + iTo.(ReferenceDereferenceInstruction).getSourceAddressOperand() = opFrom +} + +private predicate simpleOperandLocalFlowStep(Instruction iFrom, Operand opTo) { + not opTo instanceof MemoryOperand and + opTo.getDef() = iFrom +} + +private predicate modelFlow(Node nodeFrom, Node nodeTo) { + exists( + CallInstruction call, DataFlowFunction func, FunctionInput modelIn, FunctionOutput modelOut + | + call.getStaticCallTarget() = func and + func.hasDataFlow(modelIn, modelOut) + | + nodeFrom = callInput(call, modelIn) and + nodeTo = callOutput(call, modelOut) + or + exists(int d | + nodeFrom = callInput(call, modelIn, d) and + nodeTo = callOutput(call, modelOut, d) + ) + ) +} + +/** + * Holds if data flows from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +pragma[inline] +predicate localFlow(Node source, Node sink) { localFlowStep*(source, sink) } + +/** + * Holds if data can flow from `i1` to `i2` in zero or more + * local (intra-procedural) steps. + */ +pragma[inline] +predicate localInstructionFlow(Instruction e1, Instruction e2) { + localFlow(instructionNode(e1), instructionNode(e2)) +} + +/** + * Holds if data can flow from `e1` to `e2` in zero or more + * local (intra-procedural) steps. + */ +pragma[inline] +predicate localExprFlow(Expr e1, Expr e2) { localFlow(exprNode(e1), exprNode(e2)) } + +cached +private newtype TContent = + TFieldContent(Field f, int indirectionIndex) { + indirectionIndex = [1 .. Ssa::getMaxIndirectionsForType(f.getUnspecifiedType())] and + // Reads and writes of union fields are tracked using `UnionContent`. + not f.getDeclaringType() instanceof Union + } or + TUnionContent(Union u, int indirectionIndex) { + // We key `UnionContent` by the union instead of its fields since a write to one + // field can be read by any read of the union's fields. + indirectionIndex = + [1 .. max(Ssa::getMaxIndirectionsForType(u.getAField().getUnspecifiedType()))] + } or + TCollectionContent() or // Not used in C/C++ + TArrayContent() // Not used in C/C++. + +/** + * A description of the way data may be stored inside an object. Examples + * include instance fields, the contents of a collection object, or the contents + * of an array. + */ +class Content extends TContent { + /** Gets a textual representation of this element. */ + abstract string toString(); + + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 + } +} + +/** A reference through a non-union instance field. */ +class FieldContent extends Content, TFieldContent { + Field f; + int indirectionIndex; + + FieldContent() { this = TFieldContent(f, indirectionIndex) } + + override string toString() { + indirectionIndex = 1 and result = f.toString() + or + indirectionIndex > 1 and result = f.toString() + " indirection" + } + + Field getField() { result = f } + + pragma[inline] + int getIndirectionIndex() { + pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) + } +} + +/** A reference through an instance field of a union. */ +class UnionContent extends Content, TUnionContent { + Union u; + int indirectionIndex; + + UnionContent() { this = TUnionContent(u, indirectionIndex) } + + override string toString() { + indirectionIndex = 1 and result = u.toString() + or + indirectionIndex > 1 and result = u.toString() + " indirection" + } + + Field getAField() { result = u.getAField() } + + pragma[inline] + int getIndirectionIndex() { + pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) + } +} + +/** A reference through an array. */ +class ArrayContent extends Content, TArrayContent { + override string toString() { result = "[]" } +} + +/** A reference through the contents of some collection-like container. */ +private class CollectionContent extends Content, TCollectionContent { + override string toString() { result = "" } +} + +/** + * An entity that represents a set of `Content`s. + * + * The set may be interpreted differently depending on whether it is + * stored into (`getAStoreContent`) or read from (`getAReadContent`). + */ +class ContentSet instanceof Content { + /** Gets a content that may be stored into when storing into this set. */ + Content getAStoreContent() { result = this } + + /** Gets a content that may be read from when reading from this set. */ + Content getAReadContent() { result = this } + + /** Gets a textual representation of this content set. */ + string toString() { result = super.toString() } + + /** + * Holds if this element is at the specified location. + * The location spans column `startcolumn` of line `startline` to + * column `endcolumn` of line `endline` in file `filepath`. + * For more information, see + * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). + */ + predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { + super.hasLocationInfo(path, sl, sc, el, ec) + } +} + +private IRBlock getBasicBlock(Node node) { + node.asInstruction().getBlock() = result + or + node.asOperand().getUse().getBlock() = result + or + node.(SsaPhiNode).getPhiNode().getBasicBlock() = result + or + node.(IndirectOperand).getOperand().getUse().getBlock() = result + or + node.(IndirectInstruction).getInstruction().getBlock() = result + or + result = getBasicBlock(node.(PostUpdateNode).getPreUpdateNode()) +} + +/** + * Holds if the guard `g` validates the expression `e` upon evaluating to `branch`. + * + * The expression `e` is expected to be a syntactic part of the guard `g`. + * For example, the guard `g` might be a call `isSafe(x)` and the expression `e` + * the argument `x`. + */ +signature predicate guardChecksSig(IRGuardCondition g, Expr e, boolean branch); + +/** + * Provides a set of barrier nodes for a guard that validates an expression. + * + * This is expected to be used in `isBarrier`/`isSanitizer` definitions + * in data flow and taint tracking. + */ +module BarrierGuard { + /** Gets a node that is safely guarded by the given guard check. */ + ExprNode getABarrierNode() { + exists(IRGuardCondition g, Expr e, ValueNumber value, boolean edge | + e = value.getAnInstruction().getConvertedResultExpression() and + result.getConvertedExpr() = e and + guardChecks(g, value.getAnInstruction().getConvertedResultExpression(), edge) and + g.controls(getBasicBlock(result), edge) + ) + } +} + +/** + * Holds if the guard `g` validates the instruction `instr` upon evaluating to `branch`. + */ +signature predicate instructionGuardChecksSig(IRGuardCondition g, Instruction instr, boolean branch); + +/** + * Provides a set of barrier nodes for a guard that validates an instruction. + * + * This is expected to be used in `isBarrier`/`isSanitizer` definitions + * in data flow and taint tracking. + */ +module InstructionBarrierGuard { + /** Gets a node that is safely guarded by the given guard check. */ + ExprNode getABarrierNode() { + exists(IRGuardCondition g, ValueNumber value, boolean edge | + instructionGuardChecks(g, value.getAnInstruction(), edge) and + result.asInstruction() = value.getAnInstruction() and + g.controls(result.asInstruction().getBlock(), edge) + ) + } +} + +/** + * DEPRECATED: Use `BarrierGuard` module instead. + * + * A guard that validates some instruction. + * + * To use this in a configuration, extend the class and provide a + * characteristic predicate precisely specifying the guard, and override + * `checks` to specify what is being validated and in which branch. + * + * It is important that all extending classes in scope are disjoint. + */ +deprecated class BarrierGuard extends IRGuardCondition { + /** Override this predicate to hold if this guard validates `instr` upon evaluating to `b`. */ + predicate checksInstr(Instruction instr, boolean b) { none() } + + /** Override this predicate to hold if this guard validates `expr` upon evaluating to `b`. */ + predicate checks(Expr e, boolean b) { none() } + + /** Gets a node guarded by this guard. */ + final Node getAGuardedNode() { + exists(ValueNumber value, boolean edge | + ( + this.checksInstr(value.getAnInstruction(), edge) + or + this.checks(value.getAnInstruction().getConvertedResultExpression(), edge) + ) and + result.asInstruction() = value.getAnInstruction() and + this.controls(result.asInstruction().getBlock(), edge) + ) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll new file mode 100644 index 00000000000..c302c6ef878 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ModelUtil.qll @@ -0,0 +1,93 @@ +/** + * Provides predicates for mapping the `FunctionInput` and `FunctionOutput` + * classes used in function models to the corresponding instructions. + */ + +private import semmle.code.cpp.ir.IR +private import experimental.semmle.code.cpp.ir.dataflow.DataFlow +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import SsaInternals as Ssa + +/** + * Gets the instruction that goes into `input` for `call`. + */ +DataFlow::Node callInput(CallInstruction call, FunctionInput input) { + // An argument or qualifier + exists(int index | + result.asOperand() = call.getArgumentOperand(index) and + input.isParameterOrQualifierAddress(index) + ) + or + // A value pointed to by an argument or qualifier + exists(int index, int indirectionIndex | + hasOperandAndIndex(result, call.getArgumentOperand(index), indirectionIndex) and + input.isParameterDerefOrQualifierObject(index, indirectionIndex) + ) + or + exists(int ind | + result = getIndirectReturnOutNode(call, ind) and + input.isReturnValueDeref(ind) + ) +} + +/** + * Gets the instruction that holds the `output` for `call`. + */ +Node callOutput(CallInstruction call, FunctionOutput output) { + // The return value + result.asInstruction() = call and + output.isReturnValue() + or + // The side effect of a call on the value pointed to by an argument or qualifier + exists(int index, int indirectionIndex | + result.(IndirectArgumentOutNode).getArgumentIndex() = index and + result.(IndirectArgumentOutNode).getIndirectionIndex() + 1 = indirectionIndex and + result.(IndirectArgumentOutNode).getCallInstruction() = call and + output.isParameterDerefOrQualifierObject(index, indirectionIndex) + ) + or + exists(int ind | + result = getIndirectReturnOutNode(call, ind) and + output.isReturnValueDeref(ind) + ) +} + +DataFlow::Node callInput(CallInstruction call, FunctionInput input, int d) { + exists(DataFlow::Node n | n = callInput(call, input) and d > 0 | + // An argument or qualifier + hasOperandAndIndex(result, n.asOperand(), d) + or + exists(Operand operand, int indirectionIndex | + // A value pointed to by an argument or qualifier + hasOperandAndIndex(n, operand, indirectionIndex) and + hasOperandAndIndex(result, operand, indirectionIndex + d) + ) + ) +} + +private IndirectReturnOutNode getIndirectReturnOutNode(CallInstruction call, int d) { + result.getCallInstruction() = call and + result.getIndirectionIndex() = d +} + +/** + * Gets the instruction that holds the `output` for `call`. + */ +bindingset[d] +Node callOutput(CallInstruction call, FunctionOutput output, int d) { + exists(DataFlow::Node n | n = callOutput(call, output) and d > 0 | + // The return value + result = getIndirectReturnOutNode(n.asInstruction(), d) + or + // If there isn't an indirect out node for the call with indirection `d` then + // we conflate this with the underlying `CallInstruction`. + not exists(getIndirectReturnOutNode(call, d)) and + n.asInstruction() = result.asInstruction() + or + // The side effect of a call on the value pointed to by an argument or qualifier + exists(Operand operand, int indirectionIndex | + Ssa::outNodeHasAddressAndIndex(n, operand, indirectionIndex) and + Ssa::outNodeHasAddressAndIndex(result, operand, indirectionIndex + d) + ) + ) +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll new file mode 100644 index 00000000000..7359656e5a4 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRLocalFlow.qll @@ -0,0 +1,136 @@ +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import PrintIRUtilities + +/** + * Gets the local dataflow from other nodes in the same function to this node. + */ +private string getFromFlow(DataFlow::Node useNode, int order1, int order2) { + exists(DataFlow::Node defNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if defNode.asInstruction() = useNode.asOperand().getAnyDef() + then + // Shorthand for flow from the def of this operand. + result = prefix + "def" and + order1 = -1 and + order2 = 0 + else + if defNode.asOperand().getUse() = useNode.asInstruction() + then + // Shorthand for flow from an operand of this instruction + result = prefix + defNode.asOperand().getDumpId() and + order1 = -1 and + order2 = defNode.asOperand().getDumpSortOrder() + else result = prefix + nodeId(defNode, order1, order2) + ) +} + +/** + * Gets the local dataflow from this node to other nodes in the same function. + */ +private string getToFlow(DataFlow::Node defNode, int order1, int order2) { + exists(DataFlow::Node useNode, string prefix | + ( + simpleLocalFlowStep(defNode, useNode) and prefix = "" + or + any(DataFlow::Configuration cfg).isAdditionalFlowStep(defNode, useNode) and + defNode.getEnclosingCallable() = useNode.getEnclosingCallable() and + prefix = "+" + ) and + if useNode.asInstruction() = defNode.asOperand().getUse() + then + // Shorthand for flow to this operand's instruction. + result = prefix + "result" and + order1 = -1 and + order2 = 0 + else result = prefix + nodeId(useNode, order1, order2) + ) +} + +/** + * Gets the properties of the dataflow node `node`. + */ +private string getNodeProperty(DataFlow::Node node, string key) { + // List dataflow into and out of this node. Flow into this node is printed as `src->@`, and flow + // out of this node is printed as `@->dest`. + key = "flow" and + result = + strictconcat(string flow, boolean to, int order1, int order2 | + flow = getFromFlow(node, order1, order2) + "->@" and to = false + or + flow = "@->" + getToFlow(node, order1, order2) and to = true + | + flow, ", " order by to, order1, order2, flow + ) + or + // Is this node a dataflow sink? + key = "sink" and + any(DataFlow::Configuration cfg).isSink(node) and + result = "true" + or + // Is this node a dataflow source? + key = "source" and + any(DataFlow::Configuration cfg).isSource(node) and + result = "true" + or + // Is this node a dataflow barrier, and if so, what kind? + key = "barrier" and + result = + strictconcat(string kind | + any(DataFlow::Configuration cfg).isBarrier(node) and kind = "full" + or + any(DataFlow::Configuration cfg).isBarrierIn(node) and kind = "in" + or + any(DataFlow::Configuration cfg).isBarrierOut(node) and kind = "out" + | + kind, ", " + ) + or + // Is there partial flow from a source to this node? + // This property will only be emitted if partial flow is enabled by overriding + // `DataFlow::Configration::explorationLimit()`. + key = "pflow" and + result = + strictconcat(DataFlow::PartialPathNode sourceNode, DataFlow::PartialPathNode destNode, int dist, + int order1, int order2 | + any(DataFlow::Configuration cfg).hasPartialFlow(sourceNode, destNode, dist) and + destNode.getNode() = node and + // Only print flow from a source in the same function. + sourceNode.getNode().getEnclosingCallable() = node.getEnclosingCallable() + | + nodeId(sourceNode.getNode(), order1, order2) + "+" + dist.toString(), ", " + order by + order1, order2, dist desc + ) +} + +/** + * Property provider for local IR dataflow. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getOperandProperty(Operand operand, string key) { + exists(DataFlow::Node node | + operand = node.asOperand() and + result = getNodeProperty(node, key) + ) + } + + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node node | + instruction = node.asInstruction() and + result = getNodeProperty(node, key) + ) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll new file mode 100644 index 00000000000..8c318216217 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRStoreSteps.qll @@ -0,0 +1,33 @@ +/** + * Print the dataflow local store steps in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow +private import semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import PrintIRUtilities + +/** + * Property provider for local IR dataflow store steps. + */ +class LocalFlowPropertyProvider extends IRPropertyProvider { + override string getInstructionProperty(Instruction instruction, string key) { + exists(DataFlow::Node objectNode, Content content | + key = "content[" + content.toString() + "]" and + instruction = objectNode.asInstruction() and + result = + strictconcat(string element, DataFlow::Node fieldNode | + storeStep(fieldNode, content, objectNode) and + element = nodeId(fieldNode, _, _) + | + element, ", " + ) + ) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll new file mode 100644 index 00000000000..5fc15cf986c --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/PrintIRUtilities.qll @@ -0,0 +1,39 @@ +/** + * Shared utilities used when printing dataflow annotations in IR dumps. + */ + +private import cpp +// The `ValueNumbering` library has to be imported right after `cpp` to ensure +// that the cached IR gets the same checksum here as it does in queries that use +// `ValueNumbering` without `DataFlow`. +private import semmle.code.cpp.ir.ValueNumbering +private import semmle.code.cpp.ir.IR +private import semmle.code.cpp.ir.dataflow.DataFlow + +/** + * Gets a short ID for an IR dataflow node. + * - For `Instruction`s, this is just the result ID of the instruction (e.g. `m128`). + * - For `Operand`s, this is the label of the operand, prefixed with the result ID of the + * instruction and a dot (e.g. `m128.left`). + * - For `Variable`s, this is the qualified name of the variable. + */ +string nodeId(DataFlow::Node node, int order1, int order2) { + exists(Instruction instruction | instruction = node.asInstruction() | + result = instruction.getResultId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + exists(Operand operand, Instruction instruction | + operand = node.asOperand() and + instruction = operand.getUse() + | + result = instruction.getResultId() + "." + operand.getDumpId() and + order1 = instruction.getBlock().getDisplayIndex() and + order2 = instruction.getDisplayIndexInBlock() + ) + or + result = "var(" + node.asVariable().getQualifiedName() + ")" and + order1 = 1000000 and + order2 = 0 +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll new file mode 100644 index 00000000000..61ff5707d77 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternals.qll @@ -0,0 +1,547 @@ +private import codeql.ssa.Ssa as SsaImplCommon +private import semmle.code.cpp.ir.IR +private import DataFlowUtil +private import DataFlowImplCommon as DataFlowImplCommon +private import semmle.code.cpp.models.interfaces.Allocation as Alloc +private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow +private import semmle.code.cpp.ir.internal.IRCppLanguage +private import DataFlowPrivate +private import ssa0.SsaInternals as SsaInternals0 +import SsaInternalsCommon + +private module SourceVariables { + int getMaxIndirectionForIRVariable(IRVariable var) { + exists(Type type, boolean isGLValue | + var.getLanguageType().hasType(type, isGLValue) and + if isGLValue = true + then result = 1 + getMaxIndirectionsForType(type) + else result = getMaxIndirectionsForType(type) + ) + } + + class BaseSourceVariable = SsaInternals0::BaseSourceVariable; + + class BaseIRVariable = SsaInternals0::BaseIRVariable; + + class BaseCallVariable = SsaInternals0::BaseCallVariable; + + cached + private newtype TSourceVariable = + TSourceIRVariable(BaseIRVariable baseVar, int ind) { + ind = [0 .. getMaxIndirectionForIRVariable(baseVar.getIRVariable())] + } or + TCallVariable(AllocationInstruction call, int ind) { + ind = [0 .. countIndirectionsForCppType(getResultLanguageType(call))] + } + + abstract class SourceVariable extends TSourceVariable { + int ind; + + bindingset[ind] + SourceVariable() { any() } + + abstract string toString(); + + int getIndirection() { result = ind } + + abstract BaseSourceVariable getBaseVariable(); + } + + class SourceIRVariable extends SourceVariable, TSourceIRVariable { + BaseIRVariable var; + + SourceIRVariable() { this = TSourceIRVariable(var, ind) } + + IRVariable getIRVariable() { result = var.getIRVariable() } + + override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() } + + override string toString() { + ind = 0 and + result = this.getIRVariable().toString() + or + ind > 0 and + result = this.getIRVariable().toString() + " indirection" + } + } + + class CallVariable extends SourceVariable, TCallVariable { + AllocationInstruction call; + + CallVariable() { this = TCallVariable(call, ind) } + + AllocationInstruction getCall() { result = call } + + override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call } + + override string toString() { + ind = 0 and + result = "Call" + or + ind > 0 and + result = "Call indirection" + } + } +} + +import SourceVariables + +predicate hasIndirectOperand(Operand op, int indirectionIndex) { + exists(CppType type, int m | + not ignoreOperand(op) and + type = getLanguageType(op) and + m = countIndirectionsForCppType(type) and + indirectionIndex = [1 .. m] + ) +} + +predicate hasIndirectInstruction(Instruction instr, int indirectionIndex) { + exists(CppType type, int m | + not ignoreInstruction(instr) and + type = getResultLanguageType(instr) and + m = countIndirectionsForCppType(type) and + indirectionIndex = [1 .. m] + ) +} + +cached +private newtype TDefOrUseImpl = + TDefImpl(Operand address, int indirectionIndex) { + isDef(_, _, address, _, _, indirectionIndex) and + // We only include the definition if the SSA pruning stage + // concluded that the definition is live after the write. + any(SsaInternals0::Def def).getAddressOperand() = address + } or + TUseImpl(Operand operand, int indirectionIndex) { + isUse(_, operand, _, _, indirectionIndex) and + not isDef(_, _, operand, _, _, _) + } + +abstract private class DefOrUseImpl extends TDefOrUseImpl { + /** Gets a textual representation of this element. */ + abstract string toString(); + + /** Gets the block of this definition or use. */ + abstract IRBlock getBlock(); + + /** Holds if this definition or use has index `index` in block `block`. */ + abstract predicate hasIndexInBlock(IRBlock block, int index); + + final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) { + this.hasIndexInBlock(block, index) and + sv = this.getSourceVariable() + } + + /** Gets the location of this element. */ + abstract Cpp::Location getLocation(); + + /** + * Gets the index (i.e., the number of loads required) of this + * definition or use. + * + * Note that this is _not_ the definition's (or use's) index in + * the enclosing basic block. To obtain this index, use + * `DefOrUseImpl::hasIndexInBlock/2` or `DefOrUseImpl::hasIndexInBlock/3`. + */ + abstract int getIndirectionIndex(); + + /** + * Gets the instruction that computes the base of this definition or use. + * This is always a `VariableAddressInstruction` or an `AllocationInstruction`. + */ + abstract Instruction getBase(); + + final BaseSourceVariable getBaseSourceVariable() { + exists(IRVariable var | + result.(BaseIRVariable).getIRVariable() = var and + instructionHasIRVariable(this.getBase(), var) + ) + or + result.(BaseCallVariable).getCallInstruction() = this.getBase() + } + + /** Gets the variable that is defined or used. */ + final SourceVariable getSourceVariable() { + exists(BaseSourceVariable v, int ind | + sourceVariableHasBaseAndIndex(result, v, ind) and + defOrUseHasSourceVariable(this, v, ind) + ) + } +} + +pragma[noinline] +private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) { + vai.getIRVariable() = var +} + +private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv, int ind) { + defHasSourceVariable(defOrUse, bv, ind) + or + useHasSourceVariable(defOrUse, bv, ind) +} + +pragma[noinline] +private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv, int ind) { + bv = def.getBaseSourceVariable() and + ind = def.getIndirection() +} + +pragma[noinline] +private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv, int ind) { + bv = use.getBaseSourceVariable() and + ind = use.getIndirection() +} + +pragma[noinline] +private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv, int ind) { + v.getBaseVariable() = bv and + v.getIndirection() = ind +} + +class DefImpl extends DefOrUseImpl, TDefImpl { + Operand address; + int ind; + + DefImpl() { this = TDefImpl(address, ind) } + + override Instruction getBase() { isDef(_, _, address, result, _, _) } + + Operand getAddressOperand() { result = address } + + int getIndirection() { isDef(_, _, address, _, result, ind) } + + override int getIndirectionIndex() { result = ind } + + Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) } + + override string toString() { result = "DefImpl" } + + override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() } + + override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() } + + final override predicate hasIndexInBlock(IRBlock block, int index) { + this.getDefiningInstruction() = block.getInstruction(index) + } + + predicate isCertain() { isDef(true, _, address, _, _, ind) } +} + +class UseImpl extends DefOrUseImpl, TUseImpl { + Operand operand; + int ind; + + UseImpl() { this = TUseImpl(operand, ind) } + + Operand getOperand() { result = operand } + + override string toString() { result = "UseImpl" } + + final override predicate hasIndexInBlock(IRBlock block, int index) { + operand.getUse() = block.getInstruction(index) + } + + final override IRBlock getBlock() { result = operand.getUse().getBlock() } + + final override Cpp::Location getLocation() { result = operand.getLocation() } + + final int getIndirection() { isUse(_, operand, _, result, ind) } + + override int getIndirectionIndex() { result = ind } + + override Instruction getBase() { isUse(_, operand, result, _, ind) } + + predicate isCertain() { isUse(true, operand, _, _, ind) } +} + +/** + * Holds if `defOrUse1` is a definition which is first read by `use`, + * or if `defOrUse1` is a use and `use` is a next subsequent use. + * + * In both cases, `use` can either be an explicit use written in the + * source file, or it can be a phi node as computed by the SSA library. + */ +predicate adjacentDefRead(DefOrUse defOrUse1, UseOrPhi use) { + exists(IRBlock bb1, int i1, SourceVariable v | + defOrUse1.asDefOrUse().hasIndexInBlock(bb1, i1, v) + | + exists(IRBlock bb2, int i2 | + adjacentDefRead(_, pragma[only_bind_into](bb1), pragma[only_bind_into](i1), + pragma[only_bind_into](bb2), pragma[only_bind_into](i2)) + | + use.asDefOrUse().(UseImpl).hasIndexInBlock(bb2, i2, v) + ) + or + exists(PhiNode phi | + lastRefRedef(_, bb1, i1, phi) and + use.asPhi() = phi and + phi.getSourceVariable() = pragma[only_bind_into](v) + ) + ) +} + +private predicate useToNode(UseOrPhi use, Node nodeTo) { + exists(UseImpl useImpl | + useImpl = use.asDefOrUse() and + nodeHasOperand(nodeTo, useImpl.getOperand(), useImpl.getIndirectionIndex()) + ) + or + nodeTo.(SsaPhiNode).getPhiNode() = use.asPhi() +} + +pragma[noinline] +predicate outNodeHasAddressAndIndex( + IndirectArgumentOutNode out, Operand address, int indirectionIndex +) { + out.getAddressOperand() = address and + out.getIndirectionIndex() = indirectionIndex +} + +private predicate defToNode(Node nodeFrom, Def def) { + nodeHasInstruction(nodeFrom, def.getDefiningInstruction(), def.getIndirectionIndex()) +} + +private predicate nodeToDefOrUse(Node nodeFrom, SsaDefOrUse defOrUse) { + // Node -> Def + defToNode(nodeFrom, defOrUse) + or + // Node -> Use + useToNode(defOrUse, nodeFrom) +} + +/** + * Perform a single conversion-like step from `nFrom` to `nTo`. This relation + * only holds when there is no use-use relation out of `nTo`. + */ +private predicate indirectConversionFlowStep(Node nFrom, Node nTo) { + not exists(UseOrPhi defOrUse | + nodeToDefOrUse(nTo, defOrUse) and + adjacentDefRead(defOrUse, _) + ) and + exists(Operand op1, Operand op2, int indirectionIndex, Instruction instr | + hasOperandAndIndex(nFrom, op1, pragma[only_bind_into](indirectionIndex)) and + hasOperandAndIndex(nTo, op2, pragma[only_bind_into](indirectionIndex)) and + instr = op2.getDef() and + conversionFlow(op1, instr, _) + ) +} + +/** + * The reason for this predicate is a bit annoying: + * We cannot mark a `PointerArithmeticInstruction` that computes an offset based on some SSA + * variable `x` as a use of `x` since this creates taint-flow in the following example: + * ```c + * int x = array[source] + * sink(*array) + * ``` + * This is because `source` would flow from the operand of `PointerArithmeticInstruction` to the + * result of the instruction, and into the `IndirectOperand` that represents the value of `*array`. + * Then, via use-use flow, flow will arrive at `*array` in `sink(*array)`. + * + * So this predicate recurses back along conversions and `PointerArithmeticInstruction`s to find the + * first use that has provides use-use flow, and uses that target as the target of the `nodeFrom`. + */ +private predicate adjustForPointerArith(Node nodeFrom, UseOrPhi use) { + nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and + exists(DefOrUse defOrUse, Node adjusted | + indirectConversionFlowStep*(adjusted, nodeFrom) and + nodeToDefOrUse(adjusted, defOrUse) and + adjacentDefRead(defOrUse, use) + ) +} + +/** Holds if there is def-use or use-use flow from `nodeFrom` to `nodeTo`. */ +predicate ssaFlow(Node nodeFrom, Node nodeTo) { + // `nodeFrom = any(PostUpdateNode pun).getPreUpdateNode()` is implied by adjustedForPointerArith. + exists(UseOrPhi use | + adjustForPointerArith(nodeFrom, use) and + useToNode(use, nodeTo) + ) + or + not nodeFrom = any(PostUpdateNode pun).getPreUpdateNode() and + exists(DefOrUse defOrUse1, UseOrPhi use | + nodeToDefOrUse(nodeFrom, defOrUse1) and + adjacentDefRead(defOrUse1, use) and + useToNode(use, nodeTo) + ) +} + +/** Holds if `nodeTo` receives flow from the phi node `nodeFrom`. */ +predicate fromPhiNode(SsaPhiNode nodeFrom, Node nodeTo) { + exists(PhiNode phi, SourceVariable sv, IRBlock bb1, int i1, UseOrPhi use | + phi = nodeFrom.getPhiNode() and + phi.definesAt(sv, bb1, i1) and + useToNode(use, nodeTo) + | + exists(IRBlock bb2, int i2 | + use.asDefOrUse().hasIndexInBlock(bb2, i2, sv) and + adjacentDefRead(phi, bb1, i1, bb2, i2) + ) + or + exists(PhiNode phiTo | + lastRefRedef(phi, _, _, phiTo) and + nodeTo.(SsaPhiNode).getPhiNode() = phiTo + ) + ) +} + +private SsaInternals0::SourceVariable getOldSourceVariable(SourceVariable v) { + v.getBaseVariable().(BaseIRVariable).getIRVariable() = + result.getBaseVariable().(SsaInternals0::BaseIRVariable).getIRVariable() + or + v.getBaseVariable().(BaseCallVariable).getCallInstruction() = + result.getBaseVariable().(SsaInternals0::BaseCallVariable).getCallInstruction() +} + +/** + * Holds if there is a write at index `i` in basic block `bb` to variable `v` that's + * subsequently read (as determined by the SSA pruning stage). + */ +private predicate variableWriteCand(IRBlock bb, int i, SourceVariable v) { + exists(SsaInternals0::Def def, SsaInternals0::SourceVariable v0 | + def.asDefOrUse().hasIndexInBlock(bb, i, v0) and + v0 = getOldSourceVariable(v) + ) +} + +private module SsaInput implements SsaImplCommon::InputSig { + import InputSigCommon + import SourceVariables + + /** + * Holds if the `i`'th write in block `bb` writes to the variable `v`. + * `certain` is `true` if the write is guaranteed to overwrite the entire variable. + */ + predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) { + DataFlowImplCommon::forceCachingInSameStage() and + variableWriteCand(bb, i, v) and + exists(DefImpl def | def.hasIndexInBlock(bb, i, v) | + if def.isCertain() then certain = true else certain = false + ) + } + + /** + * Holds if the `i`'th read in block `bb` reads to the variable `v`. + * `certain` is `true` if the read is guaranteed. For C++, this is always the case. + */ + predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) { + exists(UseImpl use | use.hasIndexInBlock(bb, i, v) | + if use.isCertain() then certain = true else certain = false + ) + } +} + +/** + * The final SSA predicates used for dataflow purposes. + */ +cached +module SsaCached { + /** + * Holds if `def` is accessed at index `i1` in basic block `bb1` (either a read + * or a write), `def` is read at index `i2` in basic block `bb2`, and there is a + * path between them without any read of `def`. + */ + cached + predicate adjacentDefRead(Definition def, IRBlock bb1, int i1, IRBlock bb2, int i2) { + SsaImpl::adjacentDefRead(def, bb1, i1, bb2, i2) + } + + /** + * Holds if the node at index `i` in `bb` is a last reference to SSA definition + * `def`. The reference is last because it can reach another write `next`, + * without passing through another read or write. + */ + cached + predicate lastRefRedef(Definition def, IRBlock bb, int i, Definition next) { + SsaImpl::lastRefRedef(def, bb, i, next) + } +} + +cached +private newtype TSsaDefOrUse = + TDefOrUse(DefOrUseImpl defOrUse) { + defOrUse instanceof UseImpl + or + // Like in the pruning stage, we only include definition that's live after the + // write as the final definitions computed by SSA. + exists(Definition def, SourceVariable sv, IRBlock bb, int i | + def.definesAt(sv, bb, i) and + defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv) + ) + } or + TPhi(PhiNode phi) + +abstract private class SsaDefOrUse extends TSsaDefOrUse { + string toString() { none() } + + DefOrUseImpl asDefOrUse() { none() } + + PhiNode asPhi() { none() } + + abstract Location getLocation(); +} + +class DefOrUse extends TDefOrUse, SsaDefOrUse { + DefOrUseImpl defOrUse; + + DefOrUse() { this = TDefOrUse(defOrUse) } + + final override DefOrUseImpl asDefOrUse() { result = defOrUse } + + final override Location getLocation() { result = defOrUse.getLocation() } + + final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() } + + override string toString() { result = defOrUse.toString() } +} + +class Phi extends TPhi, SsaDefOrUse { + PhiNode phi; + + Phi() { this = TPhi(phi) } + + final override PhiNode asPhi() { result = phi } + + final override Location getLocation() { result = phi.getBasicBlock().getLocation() } + + override string toString() { result = "Phi" } +} + +class UseOrPhi extends SsaDefOrUse { + UseOrPhi() { + this.asDefOrUse() instanceof UseImpl + or + this instanceof Phi + } + + final override Location getLocation() { + result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation() + } +} + +class Def extends DefOrUse { + override DefImpl defOrUse; + + Operand getAddressOperand() { result = defOrUse.getAddressOperand() } + + Instruction getAddress() { result = this.getAddressOperand().getDef() } + + /** + * This predicate ensures that joins go from `defOrUse` to the result + * instead of the other way around. + */ + pragma[inline] + int getIndirectionIndex() { + pragma[only_bind_into](result) = pragma[only_bind_out](defOrUse).getIndirectionIndex() + } + + Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() } +} + +private module SsaImpl = SsaImplCommon::Make; + +class PhiNode = SsaImpl::PhiNode; + +class Definition = SsaImpl::Definition; + +import SsaCached diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll new file mode 100644 index 00000000000..36ab036c4e5 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/SsaInternalsCommon.qll @@ -0,0 +1,268 @@ +import cpp as Cpp +import semmle.code.cpp.ir.IR +import semmle.code.cpp.ir.internal.IRCppLanguage +private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects +private import DataFlowImplCommon as DataFlowImplCommon +private import DataFlowUtil + +/** + * Holds if `operand` is an operand that is not used by the dataflow library. + * Ignored operands are not recognizd as uses by SSA, and they don't have a + * corresponding `(Indirect)OperandNode`. + */ +predicate ignoreOperand(Operand operand) { + operand = any(Instruction instr | ignoreInstruction(instr)).getAnOperand() +} + +/** + * Holds if `instr` is an instruction that is not used by the dataflow library. + * Ignored instructions are not recognized as reads/writes by SSA, and they + * don't have a corresponding `(Indirect)InstructionNode`. + */ +predicate ignoreInstruction(Instruction instr) { + DataFlowImplCommon::forceCachingInSameStage() and + ( + instr instanceof WriteSideEffectInstruction or + instr instanceof PhiInstruction or + instr instanceof ReadSideEffectInstruction or + instr instanceof ChiInstruction or + instr instanceof InitializeIndirectionInstruction + ) +} + +/** + * Gets the C++ type of `this` in the member function `f`. + * The result is a glvalue if `isGLValue` is true, and + * a prvalue if `isGLValue` is false. + */ +bindingset[isGLValue] +private CppType getThisType(Cpp::MemberFunction f, boolean isGLValue) { + result.hasType(f.getTypeOfThis(), isGLValue) +} + +/** + * Gets the C++ type of the instruction `i`. + * + * This is equivalent to `i.getResultLanguageType()` with the exception + * of instructions that directly references a `this` IRVariable. In this + * case, `i.getResultLanguageType()` gives an unknown type, whereas the + * predicate gives the expected type (i.e., a potentially cv-qualified + * type `A*` where `A` is the declaring type of the member function that + * contains `i`). + */ +cached +CppType getResultLanguageType(Instruction i) { + if i.(VariableAddressInstruction).getIRVariable() instanceof IRThisVariable + then + if i.isGLValue() + then result = getThisType(i.getEnclosingFunction(), true) + else result = getThisType(i.getEnclosingFunction(), false) + else result = i.getResultLanguageType() +} + +/** + * Gets the C++ type of the operand `operand`. + * This is equivalent to the type of the operand's defining instruction. + * + * See `getResultLanguageType` for a description of this behavior. + */ +CppType getLanguageType(Operand operand) { result = getResultLanguageType(operand.getDef()) } + +/** + * Gets the maximum number of indirections a glvalue of type `type` can have. + * For example: + * - If `type = int`, the result is 1 + * - If `type = MyStruct`, the result is 1 + * - If `type = char*`, the result is 2 + */ +int getMaxIndirectionsForType(Type type) { + result = countIndirectionsForCppType(getTypeForGLValue(type)) +} + +/** + * Gets the maximum number of indirections a value of type `type` can have. + * + * Note that this predicate is intended to be called on unspecified types + * (i.e., `countIndirections(e.getUnspecifiedType())`). + */ +private int countIndirections(Type t) { + result = + 1 + + countIndirections([t.(Cpp::PointerType).getBaseType(), t.(Cpp::ReferenceType).getBaseType()]) + or + not t instanceof Cpp::PointerType and + not t instanceof Cpp::ReferenceType and + result = 0 +} + +/** + * Gets the maximum number of indirections a value of C++ + * type `langType` can have. + */ +int countIndirectionsForCppType(LanguageType langType) { + exists(Type type | langType.hasType(type, true) | + result = 1 + countIndirections(type.getUnspecifiedType()) + ) + or + exists(Type type | langType.hasType(type, false) | + result = countIndirections(type.getUnspecifiedType()) + ) +} + +/** + * A `CallInstruction` that calls an allocation function such + * as `malloc` or `operator new`. + */ +class AllocationInstruction extends CallInstruction { + AllocationInstruction() { this.getStaticCallTarget() instanceof Cpp::AllocationFunction } +} + +/** + * Holds if `i` is a base instruction that starts a sequence of uses + * of some variable that SSA can handle. + * + * This is either when `i` is a `VariableAddressInstruction` or when + * `i` is a fresh allocation produced by an `AllocationInstruction`. + */ +private predicate isSourceVariableBase(Instruction i) { + i instanceof VariableAddressInstruction or i instanceof AllocationInstruction +} + +/** + * Holds if the value pointed to by `operand` can potentially be + * modified be the caller. + */ +predicate isModifiableByCall(ArgumentOperand operand) { + exists(CallInstruction call, int index, CppType type | + type = getLanguageType(operand) and + call.getArgumentOperand(index) = operand and + if index = -1 + then not call.getStaticCallTarget() instanceof Cpp::ConstMemberFunction + else not SideEffects::isConstPointerLike(any(Type t | type.hasType(t, _))) + ) +} + +cached +private module Cached { + /** + * Holds if `op` is a use of an SSA variable rooted at `base` with `ind` number + * of indirections. + * + * `certain` is `true` if the operand is guaranteed to read the variable, and + * `indirectionIndex` specifies the number of loads required to read the variable. + */ + cached + predicate isUse(boolean certain, Operand op, Instruction base, int ind, int indirectionIndex) { + not ignoreOperand(op) and + certain = true and + exists(LanguageType type, int m, int ind0 | + type = getLanguageType(op) and + m = countIndirectionsForCppType(type) and + isUseImpl(op, base, ind0) and + ind = ind0 + [0 .. m] and + indirectionIndex = ind - ind0 + ) + } + + /** + * Holds if `operand` is a use of an SSA variable rooted at `base`, and the + * path from `base` to `operand` passes through `ind` load-like instructions. + */ + private predicate isUseImpl(Operand operand, Instruction base, int ind) { + DataFlowImplCommon::forceCachingInSameStage() and + ind = 0 and + operand.getDef() = base and + isSourceVariableBase(base) + or + exists(Operand mid, Instruction instr | + isUseImpl(mid, base, ind) and + instr = operand.getDef() and + conversionFlow(mid, instr, false) + ) + or + exists(int ind0 | + isUseImpl(operand.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0) + or + isUseImpl(operand.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0) + | + ind0 = ind - 1 + ) + } + + /** + * Holds if `address` is an address of an SSA variable rooted at `base`, + * and `instr` is a definition of the SSA variable with `ind` number of indirections. + * + * `certain` is `true` if `instr` is guaranteed to write to the variable, and + * `indirectionIndex` specifies the number of loads required to read the variable + * after the write operation. + */ + cached + predicate isDef( + boolean certain, Instruction instr, Operand address, Instruction base, int ind, + int indirectionIndex + ) { + certain = true and + exists(int ind0, CppType type, int m | + address = + [ + instr.(StoreInstruction).getDestinationAddressOperand(), + instr.(InitializeParameterInstruction).getAnOperand(), + instr.(InitializeDynamicAllocationInstruction).getAllocationAddressOperand(), + instr.(UninitializedInstruction).getAnOperand() + ] + | + isDefImpl(address, base, ind0) and + type = getLanguageType(address) and + m = countIndirectionsForCppType(type) and + ind = ind0 + [1 .. m] and + indirectionIndex = ind - (ind0 + 1) + ) + } + + /** + * Holds if `address` is a use of an SSA variable rooted at `base`, and the + * path from `base` to `address` passes through `ind` load-like instructions. + * + * Note: Unlike `isUseImpl`, this predicate recurses through pointer-arithmetic + * instructions. + */ + private predicate isDefImpl(Operand address, Instruction base, int ind) { + DataFlowImplCommon::forceCachingInSameStage() and + ind = 0 and + address.getDef() = base and + isSourceVariableBase(base) + or + exists(Operand mid, Instruction instr | + isDefImpl(mid, base, ind) and + instr = address.getDef() and + conversionFlow(mid, instr, _) + ) + or + exists(int ind0 | + isDefImpl(address.getDef().(LoadInstruction).getSourceAddressOperand(), base, ind0) + or + isDefImpl(address.getDef().(InitializeParameterInstruction).getAnOperand(), base, ind0) + | + ind0 = ind - 1 + ) + } +} + +import Cached + +/** + * Inputs to the shared SSA library's parameterized module that is shared + * between the SSA pruning stage, and the final SSA stage. + */ +module InputSigCommon { + class BasicBlock = IRBlock; + + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { result.immediatelyDominates(bb) } + + BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } + + class ExitBasicBlock extends IRBlock { + ExitBasicBlock() { this.getLastInstruction() instanceof ExitFunctionInstruction } + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll new file mode 100644 index 00000000000..1ab232923bf --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/TaintTrackingUtil.qll @@ -0,0 +1,208 @@ +private import semmle.code.cpp.ir.IR +private import experimental.semmle.code.cpp.ir.dataflow.DataFlow +private import ModelUtil +private import semmle.code.cpp.models.interfaces.DataFlow +private import semmle.code.cpp.models.interfaces.SideEffect +private import DataFlowUtil +private import DataFlowPrivate +private import semmle.code.cpp.models.Models + +/** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +predicate localTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + DataFlow::localFlowStep(nodeFrom, nodeTo) + or + localAdditionalTaintStep(nodeFrom, nodeTo) +} + +/** + * Holds if taint can flow in one local step from `nodeFrom` to `nodeTo` excluding + * local data flow steps. That is, `nodeFrom` and `nodeTo` are likely to represent + * different objects. + */ +cached +predicate localAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { + operandToInstructionTaintStep(nodeFrom.asOperand(), nodeTo.asInstruction()) + or + modeledTaintStep(nodeFrom, nodeTo) + or + // Flow from `op` to `*op`. + exists(Operand operand, int indirectionIndex | + nodeHasOperand(nodeFrom, operand, indirectionIndex) and + nodeHasOperand(nodeTo, operand, indirectionIndex - 1) + ) + or + // Flow from `instr` to `*instr`. + exists(Instruction instr, int indirectionIndex | + nodeHasInstruction(nodeFrom, instr, indirectionIndex) and + nodeHasInstruction(nodeTo, instr, indirectionIndex - 1) + ) + or + // Flow from (the indirection of) an operand of a pointer arithmetic instruction to the + // indirection of the pointer arithmetic instruction. This provides flow from `source` + // in `x[source]` to the result of the associated load instruction. + exists(PointerArithmeticInstruction pai, int indirectionIndex | + nodeHasOperand(nodeFrom, pai.getAnOperand(), pragma[only_bind_into](indirectionIndex)) and + hasInstructionAndIndex(nodeTo, pai, indirectionIndex + 1) + ) +} + +/** + * Holds if taint propagates from `nodeFrom` to `nodeTo` in exactly one local + * (intra-procedural) step. + */ +private predicate operandToInstructionTaintStep(Operand opFrom, Instruction instrTo) { + // Taint can flow through expressions that alter the value but preserve + // more than one bit of it _or_ expressions that follow data through + // pointer indirections. + instrTo.getAnOperand() = opFrom and + ( + instrTo instanceof ArithmeticInstruction + or + instrTo instanceof BitwiseInstruction + or + instrTo instanceof PointerArithmeticInstruction + ) + or + // The `CopyInstruction` case is also present in non-taint data flow, but + // that uses `getDef` rather than `getAnyDef`. For taint, we want flow + // from a definition of `myStruct` to a `myStruct.myField` expression. + instrTo.(LoadInstruction).getSourceAddressOperand() = opFrom + or + // Unary instructions tend to preserve enough information in practice that we + // want taint to flow through. + // The exception is `FieldAddressInstruction`. Together with the rules below for + // `LoadInstruction`s and `ChiInstruction`s, flow through `FieldAddressInstruction` + // could cause flow into one field to come out an unrelated field. + // This would happen across function boundaries, where the IR would not be able to + // match loads to stores. + instrTo.(UnaryInstruction).getUnaryOperand() = opFrom and + ( + not instrTo instanceof FieldAddressInstruction + or + instrTo.(FieldAddressInstruction).getField().getDeclaringType() instanceof Union + ) +} + +/** + * Holds if taint may propagate from `source` to `sink` in zero or more local + * (intra-procedural) steps. + */ +pragma[inline] +predicate localTaint(DataFlow::Node source, DataFlow::Node sink) { localTaintStep*(source, sink) } + +/** + * Holds if taint can flow from `i1` to `i2` in zero or more + * local (intra-procedural) steps. + */ +pragma[inline] +predicate localInstructionTaint(Instruction i1, Instruction i2) { + localTaint(DataFlow::instructionNode(i1), DataFlow::instructionNode(i2)) +} + +/** + * Holds if taint can flow from `e1` to `e2` in zero or more + * local (intra-procedural) steps. + */ +pragma[inline] +predicate localExprTaint(Expr e1, Expr e2) { + localTaint(DataFlow::exprNode(e1), DataFlow::exprNode(e2)) +} + +/** + * Holds if the additional step from `src` to `sink` should be included in all + * global taint flow configurations. + */ +predicate defaultAdditionalTaintStep(DataFlow::Node src, DataFlow::Node sink) { + localAdditionalTaintStep(src, sink) +} + +/** + * Holds if default `TaintTracking::Configuration`s should allow implicit reads + * of `c` at sinks and inputs to additional taint steps. + */ +bindingset[node] +predicate defaultImplicitTaintRead(DataFlow::Node node, DataFlow::Content c) { none() } + +/** + * Holds if `node` should be a sanitizer in all global taint flow configurations + * but not in local taint. + */ +predicate defaultTaintSanitizer(DataFlow::Node node) { none() } + +/** + * Holds if taint can flow from `instrIn` to `instrOut` through a call to a + * modeled function. + */ +predicate modeledTaintStep(DataFlow::Node nodeIn, DataFlow::Node nodeOut) { + // Normal taint steps + exists(CallInstruction call, TaintFunction func, FunctionInput modelIn, FunctionOutput modelOut | + call.getStaticCallTarget() = func and + func.hasTaintFlow(modelIn, modelOut) + | + ( + nodeIn = callInput(call, modelIn) + or + exists(int n | + modelIn.isParameterDerefOrQualifierObject(n) and + if n = -1 + then nodeIn = callInput(call, any(InQualifierAddress inQualifier)) + else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n)) + ) + ) and + nodeOut = callOutput(call, modelOut) + or + exists(int d | + nodeIn = callInput(call, modelIn, d) + or + exists(int n | + d = 1 and + modelIn.isParameterDerefOrQualifierObject(n) and + if n = -1 + then nodeIn = callInput(call, any(InQualifierAddress inQualifier)) + else nodeIn = callInput(call, any(InParameter inParam | inParam.getIndex() = n)) + ) + | + call.getStaticCallTarget() = func and + func.hasTaintFlow(modelIn, modelOut) and + nodeOut = callOutput(call, modelOut, d) + ) + ) + or + // Taint flow from one argument to another and data flow from an argument to a + // return value. This happens in functions like `strcat` and `memcpy`. We + // could model this flow in two separate steps, but that would add reverse + // flow from the write side-effect to the call instruction, which may not be + // desirable. + exists( + CallInstruction call, Function func, FunctionInput modelIn, OutParameterDeref modelMidOut, + int indexMid, InParameter modelMidIn, OutReturnValue modelOut + | + nodeIn = callInput(call, modelIn) and + nodeOut = callOutput(call, modelOut) and + call.getStaticCallTarget() = func and + func.(TaintFunction).hasTaintFlow(modelIn, modelMidOut) and + func.(DataFlowFunction).hasDataFlow(modelMidIn, modelOut) and + modelMidOut.isParameterDeref(indexMid) and + modelMidIn.isParameter(indexMid) + ) + or + // Taint flow from a pointer argument to an output, when the model specifies flow from the deref + // to that output, but the deref is not modeled in the IR for the caller. + exists( + CallInstruction call, DataFlow::SideEffectOperandNode indirectArgument, Function func, + FunctionInput modelIn, FunctionOutput modelOut + | + indirectArgument = callInput(call, modelIn) and + indirectArgument.getAddressOperand() = nodeIn.asOperand() and + call.getStaticCallTarget() = func and + ( + func.(DataFlowFunction).hasDataFlow(modelIn, modelOut) + or + func.(TaintFunction).hasTaintFlow(modelIn, modelOut) + ) and + nodeOut = callOutput(call, modelOut) + ) +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ssa0/SsaInternals.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ssa0/SsaInternals.qll new file mode 100644 index 00000000000..3a89f1d170f --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/ssa0/SsaInternals.qll @@ -0,0 +1,314 @@ +/** + * This module defines an initial SSA pruning stage that doesn't take + * indirections into account. + */ + +private import codeql.ssa.Ssa as SsaImplCommon +private import semmle.code.cpp.ir.IR +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon +private import semmle.code.cpp.models.interfaces.Allocation as Alloc +private import semmle.code.cpp.models.interfaces.DataFlow as DataFlow +private import semmle.code.cpp.ir.implementation.raw.internal.SideEffects as SideEffects +private import semmle.code.cpp.ir.internal.IRCppLanguage +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowPrivate +private import experimental.semmle.code.cpp.ir.dataflow.internal.DataFlowUtil +private import experimental.semmle.code.cpp.ir.dataflow.internal.SsaInternalsCommon + +private module SourceVariables { + newtype TBaseSourceVariable = + // Each IR variable gets its own source variable + TBaseIRVariable(IRVariable var) or + // Each allocation gets its own source variable + TBaseCallVariable(AllocationInstruction call) + + abstract class BaseSourceVariable extends TBaseSourceVariable { + abstract string toString(); + + abstract DataFlowType getType(); + } + + class BaseIRVariable extends BaseSourceVariable, TBaseIRVariable { + IRVariable var; + + IRVariable getIRVariable() { result = var } + + BaseIRVariable() { this = TBaseIRVariable(var) } + + override string toString() { result = var.toString() } + + override DataFlowType getType() { result = var.getIRType() } + } + + class BaseCallVariable extends BaseSourceVariable, TBaseCallVariable { + AllocationInstruction call; + + BaseCallVariable() { this = TBaseCallVariable(call) } + + AllocationInstruction getCallInstruction() { result = call } + + override string toString() { result = call.toString() } + + override DataFlowType getType() { result = call.getResultIRType() } + } + + private newtype TSourceVariable = + TSourceIRVariable(BaseIRVariable baseVar) or + TCallVariable(AllocationInstruction call) + + abstract class SourceVariable extends TSourceVariable { + abstract string toString(); + + abstract BaseSourceVariable getBaseVariable(); + } + + class SourceIRVariable extends SourceVariable, TSourceIRVariable { + BaseIRVariable var; + + SourceIRVariable() { this = TSourceIRVariable(var) } + + IRVariable getIRVariable() { result = var.getIRVariable() } + + override BaseIRVariable getBaseVariable() { result.getIRVariable() = this.getIRVariable() } + + override string toString() { result = this.getIRVariable().toString() } + } + + class CallVariable extends SourceVariable, TCallVariable { + AllocationInstruction call; + + CallVariable() { this = TCallVariable(call) } + + AllocationInstruction getCall() { result = call } + + override BaseCallVariable getBaseVariable() { result.getCallInstruction() = call } + + override string toString() { result = "Call" } + } +} + +import SourceVariables + +private newtype TDefOrUseImpl = + TDefImpl(Operand address) { isDef(_, _, address, _, _, _) } or + TUseImpl(Operand operand) { + isUse(_, operand, _, _, _) and + not isDef(_, _, operand, _, _, _) + } + +abstract private class DefOrUseImpl extends TDefOrUseImpl { + /** Gets a textual representation of this element. */ + abstract string toString(); + + /** Gets the block of this definition or use. */ + abstract IRBlock getBlock(); + + /** Holds if this definition or use has index `index` in block `block`. */ + abstract predicate hasIndexInBlock(IRBlock block, int index); + + final predicate hasIndexInBlock(IRBlock block, int index, SourceVariable sv) { + this.hasIndexInBlock(block, index) and + sv = this.getSourceVariable() + } + + /** Gets the location of this element. */ + abstract Cpp::Location getLocation(); + + abstract Instruction getBase(); + + final BaseSourceVariable getBaseSourceVariable() { + exists(IRVariable var | + result.(BaseIRVariable).getIRVariable() = var and + instructionHasIRVariable(this.getBase(), var) + ) + or + result.(BaseCallVariable).getCallInstruction() = this.getBase() + } + + /** Gets the variable that is defined or used. */ + final SourceVariable getSourceVariable() { + exists(BaseSourceVariable v | + sourceVariableHasBaseAndIndex(result, v) and + defOrUseHasSourceVariable(this, v) + ) + } +} + +pragma[noinline] +private predicate instructionHasIRVariable(VariableAddressInstruction vai, IRVariable var) { + vai.getIRVariable() = var +} + +private predicate defOrUseHasSourceVariable(DefOrUseImpl defOrUse, BaseSourceVariable bv) { + defHasSourceVariable(defOrUse, bv) + or + useHasSourceVariable(defOrUse, bv) +} + +pragma[noinline] +private predicate defHasSourceVariable(DefImpl def, BaseSourceVariable bv) { + bv = def.getBaseSourceVariable() +} + +pragma[noinline] +private predicate useHasSourceVariable(UseImpl use, BaseSourceVariable bv) { + bv = use.getBaseSourceVariable() +} + +pragma[noinline] +private predicate sourceVariableHasBaseAndIndex(SourceVariable v, BaseSourceVariable bv) { + v.getBaseVariable() = bv +} + +class DefImpl extends DefOrUseImpl, TDefImpl { + Operand address; + + DefImpl() { this = TDefImpl(address) } + + override Instruction getBase() { isDef(_, _, address, result, _, _) } + + Operand getAddressOperand() { result = address } + + Instruction getDefiningInstruction() { isDef(_, result, address, _, _, _) } + + override string toString() { result = address.toString() } + + override IRBlock getBlock() { result = this.getDefiningInstruction().getBlock() } + + override Cpp::Location getLocation() { result = this.getDefiningInstruction().getLocation() } + + final override predicate hasIndexInBlock(IRBlock block, int index) { + this.getDefiningInstruction() = block.getInstruction(index) + } + + predicate isCertain() { isDef(true, _, address, _, _, _) } +} + +class UseImpl extends DefOrUseImpl, TUseImpl { + Operand operand; + + UseImpl() { this = TUseImpl(operand) } + + Operand getOperand() { result = operand } + + override string toString() { result = operand.toString() } + + final override predicate hasIndexInBlock(IRBlock block, int index) { + operand.getUse() = block.getInstruction(index) + } + + final override IRBlock getBlock() { result = operand.getUse().getBlock() } + + final override Cpp::Location getLocation() { result = operand.getLocation() } + + override Instruction getBase() { isUse(_, operand, result, _, _) } + + predicate isCertain() { isUse(true, operand, _, _, _) } +} + +private module SsaInput implements SsaImplCommon::InputSig { + import InputSigCommon + import SourceVariables + + /** + * Holds if the `i`'th write in block `bb` writes to the variable `v`. + * `certain` is `true` if the write is guaranteed to overwrite the entire variable. + */ + predicate variableWrite(IRBlock bb, int i, SourceVariable v, boolean certain) { + DataFlowImplCommon::forceCachingInSameStage() and + exists(DefImpl def | def.hasIndexInBlock(bb, i, v) | + if def.isCertain() then certain = true else certain = false + ) + } + + /** + * Holds if the `i`'th read in block `bb` reads to the variable `v`. + * `certain` is `true` if the read is guaranteed. + */ + predicate variableRead(IRBlock bb, int i, SourceVariable v, boolean certain) { + exists(UseImpl use | use.hasIndexInBlock(bb, i, v) | + if use.isCertain() then certain = true else certain = false + ) + } +} + +private newtype TSsaDefOrUse = + TDefOrUse(DefOrUseImpl defOrUse) { + defOrUse instanceof UseImpl + or + // If `defOrUse` is a definition we only include it if the + // SSA library concludes that it's live after the write. + exists(Definition def, SourceVariable sv, IRBlock bb, int i | + def.definesAt(sv, bb, i) and + defOrUse.(DefImpl).hasIndexInBlock(bb, i, sv) + ) + } or + TPhi(PhiNode phi) + +abstract private class SsaDefOrUse extends TSsaDefOrUse { + string toString() { result = "SsaDefOrUse" } + + DefOrUseImpl asDefOrUse() { none() } + + PhiNode asPhi() { none() } + + abstract Location getLocation(); +} + +class DefOrUse extends TDefOrUse, SsaDefOrUse { + DefOrUseImpl defOrUse; + + DefOrUse() { this = TDefOrUse(defOrUse) } + + final override DefOrUseImpl asDefOrUse() { result = defOrUse } + + final override Location getLocation() { result = defOrUse.getLocation() } + + final SourceVariable getSourceVariable() { result = defOrUse.getSourceVariable() } +} + +class Phi extends TPhi, SsaDefOrUse { + PhiNode phi; + + Phi() { this = TPhi(phi) } + + final override PhiNode asPhi() { result = phi } + + final override Location getLocation() { result = phi.getBasicBlock().getLocation() } +} + +class UseOrPhi extends SsaDefOrUse { + UseOrPhi() { + this.asDefOrUse() instanceof UseImpl + or + this instanceof Phi + } + + final override Location getLocation() { + result = this.asDefOrUse().getLocation() or result = this.(Phi).getLocation() + } + + override string toString() { + result = this.asDefOrUse().toString() + or + this instanceof Phi and + result = "Phi" + } +} + +class Def extends DefOrUse { + override DefImpl defOrUse; + + Operand getAddressOperand() { result = defOrUse.getAddressOperand() } + + Instruction getAddress() { result = this.getAddressOperand().getDef() } + + Instruction getDefiningInstruction() { result = defOrUse.getDefiningInstruction() } + + override string toString() { result = this.asDefOrUse().toString() + " (def)" } +} + +private module SsaImpl = SsaImplCommon::Make; + +class PhiNode = SsaImpl::PhiNode; + +class Definition = SsaImpl::Definition; diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll new file mode 100644 index 00000000000..e6ce1ada8d4 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingImpl.qll @@ -0,0 +1,186 @@ +/** + * 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) } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } + + deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited + * when the flow state is `state`. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { + none() + } + + deprecated 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::ContentSet 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) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingParameter.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingParameter.qll new file mode 100644 index 00000000000..07185a4ad57 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking1/TaintTrackingParameter.qll @@ -0,0 +1,5 @@ +import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import experimental.semmle.code.cpp.ir.dataflow.DataFlow::DataFlow as DataFlow +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll new file mode 100644 index 00000000000..e6ce1ada8d4 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingImpl.qll @@ -0,0 +1,186 @@ +/** + * 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) } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } + + deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited + * when the flow state is `state`. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { + none() + } + + deprecated 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::ContentSet 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) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingParameter.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingParameter.qll new file mode 100644 index 00000000000..7d545fe5d04 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking2/TaintTrackingParameter.qll @@ -0,0 +1,5 @@ +import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import experimental.semmle.code.cpp.ir.dataflow.DataFlow2::DataFlow2 as DataFlow +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll new file mode 100644 index 00000000000..e6ce1ada8d4 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingImpl.qll @@ -0,0 +1,186 @@ +/** + * 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) } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard) { none() } + + deprecated final override predicate isBarrierGuard(DataFlow::BarrierGuard guard) { + this.isSanitizerGuard(guard) + } + + /** + * DEPRECATED: Use `isSanitizer` and `BarrierGuard` module instead. + * + * Holds if taint propagation through nodes guarded by `guard` is prohibited + * when the flow state is `state`. + */ + deprecated predicate isSanitizerGuard(DataFlow::BarrierGuard guard, DataFlow::FlowState state) { + none() + } + + deprecated 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::ContentSet 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) + } +} diff --git a/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll new file mode 100644 index 00000000000..3e69abcf4a7 --- /dev/null +++ b/cpp/ql/lib/experimental/semmle/code/cpp/ir/dataflow/internal/tainttracking3/TaintTrackingParameter.qll @@ -0,0 +1,5 @@ +import experimental.semmle.code.cpp.ir.dataflow.internal.TaintTrackingUtil as Public + +module Private { + import experimental.semmle.code.cpp.ir.dataflow.DataFlow3::DataFlow3 as DataFlow +} diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll index 1c75529be00..873a3c635f8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConsistency.qll @@ -1,2 +1,2 @@ -private import SSAConstruction as SSA -import SSA::SsaConsistency +private import SSAConstruction as Ssa +import Ssa::SsaConsistency diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll index 901735069c0..21c03e176a5 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/aliased_ssa/internal/SSAConstruction.qll @@ -1135,7 +1135,7 @@ deprecated module SSAConsistency = SsaConsistency; * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures * that all of SSA construction will be evaluated in the same stage. */ -module SSA { +module Ssa { class MemoryLocation = Alias::MemoryLocation; predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; @@ -1144,3 +1144,6 @@ module SSA { predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } + +/** DEPRECATED: Alias for Ssa */ +deprecated module SSA = Ssa; diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll index b30372a791b..5564a16f215 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/internal/TInstruction.qll @@ -20,24 +20,24 @@ newtype TInstruction = IRConstruction::Raw::hasInstruction(tag1, tag2) } or TUnaliasedSsaPhiInstruction( - TRawInstruction blockStartInstr, UnaliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation ) { - UnaliasedSsa::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + UnaliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation) } or TUnaliasedSsaChiInstruction(TRawInstruction primaryInstruction) { none() } or TUnaliasedSsaUnreachedInstruction(IRFunctionBase irFunc) { - UnaliasedSsa::SSA::hasUnreachedInstruction(irFunc) + UnaliasedSsa::Ssa::hasUnreachedInstruction(irFunc) } or TAliasedSsaPhiInstruction( - TRawInstruction blockStartInstr, AliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation ) { - AliasedSsa::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + AliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation) } or TAliasedSsaChiInstruction(TRawInstruction primaryInstruction) { - AliasedSsa::SSA::hasChiInstruction(primaryInstruction) + AliasedSsa::Ssa::hasChiInstruction(primaryInstruction) } or TAliasedSsaUnreachedInstruction(IRFunctionBase irFunc) { - AliasedSsa::SSA::hasUnreachedInstruction(irFunc) + AliasedSsa::Ssa::hasUnreachedInstruction(irFunc) } /** @@ -50,7 +50,7 @@ module UnaliasedSsaInstructions { class TPhiInstruction = TUnaliasedSsaPhiInstruction; TPhiInstruction phiInstruction( - TRawInstruction blockStartInstr, UnaliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation ) { result = TUnaliasedSsaPhiInstruction(blockStartInstr, memoryLocation) } @@ -83,7 +83,7 @@ module AliasedSsaInstructions { class TPhiInstruction = TAliasedSsaPhiInstruction or TUnaliasedSsaPhiInstruction; TPhiInstruction phiInstruction( - TRawInstruction blockStartInstr, AliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation ) { result = TAliasedSsaPhiInstruction(blockStartInstr, memoryLocation) } diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll index 93b20865cac..684aa4f14f2 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/raw/internal/SideEffects.qll @@ -55,7 +55,15 @@ private predicate isDeeplyConstBelow(Type t) { isDeeplyConstBelow(t.(TypedefType).getBaseType()) } -private predicate isConstPointerLike(Type t) { +/** + * INTERNAL: Do not use. + * + * Holds if `t` is a pointer-like type (i.e., a pointer, + * an array a reference, or a pointer-wrapper such as + * `std::unique_ptr`) that is constant and only contains + * constant types, excluding the type itself. + */ +predicate isConstPointerLike(Type t) { ( t instanceof PointerWrapper or diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll index 1c75529be00..873a3c635f8 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll @@ -1,2 +1,2 @@ -private import SSAConstruction as SSA -import SSA::SsaConsistency +private import SSAConstruction as Ssa +import Ssa::SsaConsistency diff --git a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 901735069c0..21c03e176a5 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1135,7 +1135,7 @@ deprecated module SSAConsistency = SsaConsistency; * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures * that all of SSA construction will be evaluated in the same stage. */ -module SSA { +module Ssa { class MemoryLocation = Alias::MemoryLocation; predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; @@ -1144,3 +1144,6 @@ module SSA { predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } + +/** DEPRECATED: Alias for Ssa */ +deprecated module SSA = Ssa; diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll index c93a5ad147b..8c531891bcd 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdContainer.qll @@ -176,7 +176,7 @@ private class StdSequenceContainerInsert extends TaintFunction { ) and ( output.isQualifierObject() or - output.isReturnValueDeref() + output.isReturnValue() ) } } diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll index ae190688b70..3d2eda59799 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/StdString.qll @@ -543,11 +543,11 @@ private class StdOStreamOutNonMember extends DataFlowFunction, TaintFunction { override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) { // flow from second parameter to first parameter - input.isParameter(1) and + input.isParameterDeref(1) and output.isParameterDeref(0) or // flow from second parameter to return value - input.isParameter(1) and + input.isParameterDeref(1) and output.isReturnValueDeref() or // reverse flow from returned reference to the first parameter diff --git a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll index 1a65e7b6ca4..e729c3cb0a4 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/implementations/Strcat.qll @@ -61,7 +61,7 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid input.isParameterDeref(0) and output.isParameterDeref(0) or - input.isParameter(1) and + input.isParameterDeref(1) and output.isParameterDeref(0) } diff --git a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll index 5e899be68d4..de44913a39f 100644 --- a/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll +++ b/cpp/ql/lib/semmle/code/cpp/models/interfaces/FunctionInputsAndOutputs.qll @@ -46,6 +46,26 @@ class FunctionInput extends TFunctionInput { */ deprecated final predicate isInParameter(ParameterIndex index) { this.isParameter(index) } + /** + * Holds if this is the input value pointed to (through `ind` number of indirections) by a + * pointer parameter to a function, or the input value referred to by a reference parameter + * to a function, where the parameter has index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1, 1)` holds for the `FunctionInput` that represents the value of `*p` (with + * type `char`) on entry to the function. + * - `isParameterDeref(2, 1)` holds for the `FunctionInput` that represents the value of `r` (with type + * `float`) on entry to the function. + * - There is no `FunctionInput` for which `isParameterDeref(0, _)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex index, int ind) { + ind = 1 and this.isParameterDeref(index) + } + /** * Holds if this is the input value pointed to by a pointer parameter to a function, or the input * value referred to by a reference parameter to a function, where the parameter has index @@ -62,7 +82,7 @@ class FunctionInput extends TFunctionInput { * - There is no `FunctionInput` for which `isParameterDeref(0)` holds, because `n` is neither a * pointer nor a reference. */ - predicate isParameterDeref(ParameterIndex index) { none() } + predicate isParameterDeref(ParameterIndex index) { this.isParameterDeref(index, 1) } /** * Holds if this is the input value pointed to by a pointer parameter to a function, or the input @@ -87,7 +107,22 @@ class FunctionInput extends TFunctionInput { * - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this` * (with type `C const`) on entry to the function. */ - predicate isQualifierObject() { none() } + predicate isQualifierObject(int ind) { ind = 1 and this.isQualifierObject() } + + /** + * Holds if this is the input value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r) const; + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionInput` that represents the value of `*this` + * (with type `C const`) on entry to the function. + */ + predicate isQualifierObject() { this.isQualifierObject(1) } /** * Holds if this is the input value pointed to by the `this` pointer of an instance member @@ -143,16 +178,49 @@ class FunctionInput extends TFunctionInput { * rare, but they do occur when a function returns a reference to itself, * part of itself, or one of its other inputs. */ - predicate isReturnValueDeref() { none() } + predicate isReturnValueDeref() { this.isReturnValueDeref(1) } + + /** + * Holds if this is the input value pointed to by the return value of a + * function, if the function returns a pointer, or the input value referred + * to by the return value of a function, if the function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref(1)` holds for the `FunctionInput` that represents the + * value of `*getPointer()` (with type `char`). + * - `isReturnValueDeref(1)` holds for the `FunctionInput` that represents the + * value of `getReference()` (with type `float`). + * - There is no `FunctionInput` of `getInt()` for which + * `isReturnValueDeref(_)` holds because the return type of `getInt()` is + * neither a pointer nor a reference. + * + * Note that data flows in through function return values are relatively + * rare, but they do occur when a function returns a reference to itself, + * part of itself, or one of its other inputs. + */ + predicate isReturnValueDeref(int ind) { ind = 1 and this.isReturnValueDeref() } + + /** + * Holds if `i >= 0` and `isParameterDeref(i, ind)` holds for this value, or + * if `i = -1` and `isQualifierObject(ind)` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i, int ind) { + i >= 0 and this.isParameterDeref(i, ind) + or + i = -1 and this.isQualifierObject(ind) + } /** * Holds if `i >= 0` and `isParameterDeref(i)` holds for this value, or * if `i = -1` and `isQualifierObject()` holds for this value. */ final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { - i >= 0 and this.isParameterDeref(i) - or - i = -1 and this.isQualifierObject() + this.isParameterDerefOrQualifierObject(i, 1) } } @@ -308,7 +376,25 @@ class FunctionOutput extends TFunctionOutput { * - There is no `FunctionOutput` for which `isParameterDeref(0)` holds, because `n` is neither a * pointer nor a reference. */ - predicate isParameterDeref(ParameterIndex i) { none() } + predicate isParameterDeref(ParameterIndex i) { this.isParameterDeref(i, 1) } + + /** + * Holds if this is the output value pointed to by a pointer parameter (through `ind` number + * of indirections) to a function, or the output value referred to by a reference parameter to + * a function, where the parameter has index `index`. + * + * Example: + * ``` + * void func(int n, char* p, float& r); + * ``` + * - `isParameterDeref(1, 1)` holds for the `FunctionOutput` that represents the value of `*p` (with + * type `char`) on return from the function. + * - `isParameterDeref(2, 1)` holds for the `FunctionOutput` that represents the value of `r` (with + * type `float`) on return from the function. + * - There is no `FunctionOutput` for which `isParameterDeref(0, _)` holds, because `n` is neither a + * pointer nor a reference. + */ + predicate isParameterDeref(ParameterIndex i, int ind) { ind = 1 and this.isParameterDeref(i) } /** * Holds if this is the output value pointed to by a pointer parameter to a function, or the @@ -333,7 +419,22 @@ class FunctionOutput extends TFunctionOutput { * - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this` * (with type `C`) on return from the function. */ - predicate isQualifierObject() { none() } + predicate isQualifierObject() { this.isQualifierObject(1) } + + /** + * Holds if this is the output value pointed to by the `this` pointer of an instance member + * function. + * + * Example: + * ``` + * struct C { + * void mfunc(int n, char* p, float& r); + * }; + * ``` + * - `isQualifierObject()` holds for the `FunctionOutput` that represents the value of `*this` + * (with type `C`) on return from the function. + */ + predicate isQualifierObject(int ind) { ind = 1 and this.isQualifierObject() } /** * Holds if this is the output value pointed to by the `this` pointer of an instance member @@ -385,7 +486,27 @@ class FunctionOutput extends TFunctionOutput { * - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref()` holds because the * return type of `getInt()` is neither a pointer nor a reference. */ - predicate isReturnValueDeref() { none() } + predicate isReturnValueDeref() { this.isReturnValueDeref(_) } + + /** + * Holds if this is the output value pointed to by the return value of a function, if the function + * returns a pointer, or the output value referred to by the return value of a function, if the + * function returns a reference. + * + * Example: + * ``` + * char* getPointer(); + * float& getReference(); + * int getInt(); + * ``` + * - `isReturnValueDeref(1)` holds for the `FunctionOutput` that represents the value of + * `*getPointer()` (with type `char`). + * - `isReturnValueDeref(1)` holds for the `FunctionOutput` that represents the value of + * `getReference()` (with type `float`). + * - There is no `FunctionOutput` of `getInt()` for which `isReturnValueDeref(_)` holds because the + * return type of `getInt()` is neither a pointer nor a reference. + */ + predicate isReturnValueDeref(int ind) { ind = 1 and this.isReturnValueDeref() } /** * Holds if this is the output value pointed to by the return value of a function, if the function @@ -395,14 +516,22 @@ class FunctionOutput extends TFunctionOutput { */ deprecated final predicate isOutReturnPointer() { this.isReturnValueDeref() } + /** + * Holds if `i >= 0` and `isParameterDeref(i, ind)` holds for this is the value, or + * if `i = -1` and `isQualifierObject(ind)` holds for this value. + */ + final predicate isParameterDerefOrQualifierObject(ParameterIndex i, int ind) { + i >= 0 and this.isParameterDeref(i, ind) + or + i = -1 and this.isQualifierObject(ind) + } + /** * Holds if `i >= 0` and `isParameterDeref(i)` holds for this is the value, or * if `i = -1` and `isQualifierObject()` holds for this value. */ final predicate isParameterDerefOrQualifierObject(ParameterIndex i) { - i >= 0 and this.isParameterDeref(i) - or - i = -1 and this.isQualifierObject() + this.isParameterDerefOrQualifierObject(i, 1) } } @@ -431,6 +560,10 @@ class OutParameterDeref extends FunctionOutput, TOutParameterDeref { ParameterIndex getIndex() { result = index } override predicate isParameterDeref(ParameterIndex i) { i = index } + + override predicate isParameterDeref(ParameterIndex i, int ind) { + this.isParameterDeref(i) and ind = 1 + } } /** diff --git a/cpp/ql/lib/semmle/code/cpp/pointsto/PointsTo.qll b/cpp/ql/lib/semmle/code/cpp/pointsto/PointsTo.qll index 83b8c2936f0..542ce985060 100644 --- a/cpp/ql/lib/semmle/code/cpp/pointsto/PointsTo.qll +++ b/cpp/ql/lib/semmle/code/cpp/pointsto/PointsTo.qll @@ -19,6 +19,10 @@ * `pointstoinfo` predicate determines the transitively implied points-to * information by collapsing pointers into equivalence classes. These * equivalence classes are called "points-to sets". + * + * WARNING: This library may perform poorly on very large projects. + * Consider using another library such as `semmle.code.cpp.dataflow.DataFlow` + * instead. */ import semmle.code.cpp.commons.File diff --git a/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql index 7361e875581..ca8d277cc91 100644 --- a/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql +++ b/cpp/ql/src/Architecture/General Class-Level Information/HubClasses.ql @@ -12,5 +12,5 @@ import cpp from Class c where c.fromSource() -select c as Class, c.getMetrics().getAfferentCoupling() as AfferentCoupling, - c.getMetrics().getEfferentSourceCoupling() as EfferentCoupling order by AfferentCoupling desc +select c as class_, c.getMetrics().getAfferentCoupling() as afferentCoupling, + c.getMetrics().getEfferentSourceCoupling() as efferentCoupling order by afferentCoupling desc diff --git a/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql index 0fc6f0ba2a7..693c2961e4d 100644 --- a/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql +++ b/cpp/ql/src/Architecture/General Class-Level Information/InheritanceDepthDistribution.ql @@ -16,5 +16,5 @@ predicate hasInheritanceDepth(Class c, int d) { from int depth where hasInheritanceDepth(_, depth) -select depth as InheritanceDepth, count(Class c | hasInheritanceDepth(c, depth)) as NumberOfClasses - order by InheritanceDepth +select depth as inheritanceDepth, count(Class c | hasInheritanceDepth(c, depth)) as numberOfClasses + order by inheritanceDepth diff --git a/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql index 6f842baec42..a065e4d4823 100644 --- a/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql +++ b/cpp/ql/src/Architecture/General Top-Level Information/GeneralStatistics.ql @@ -51,4 +51,4 @@ where 100 * sum(Class c | c.fromSource() | c.getMetrics().getEfferentSourceCoupling()) / sum(Class c | c.fromSource() | c.getMetrics().getEfferentCoupling()) ).toString() + "%" -select l as Title, n as Value +select l as title, n as value diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql index 384af9ebef8..a38cbbd9cb6 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ClassesWithManyDependencies.ql @@ -16,4 +16,4 @@ where t.fromSource() and n = t.getMetrics().getEfferentSourceCoupling() and n > 10 -select t as Class, "This class has too many dependencies (" + n.toString() + ")" +select t as class_, "This class has too many dependencies (" + n.toString() + ")" diff --git a/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql index 5fe0585131c..ff4ce9fbda1 100644 --- a/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql +++ b/cpp/ql/src/Architecture/Refactoring Opportunities/ComplexFunctions.ql @@ -17,4 +17,4 @@ where n = f.getMetrics().getNumberOfCalls() and n > 99 and not f.isMultiplyDefined() -select f as Function, "This function makes too many calls (" + n.toString() + ")" +select f as function, "This function makes too many calls (" + n.toString() + ")" diff --git a/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql index 2d1440813f4..a468ce4a46d 100644 --- a/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/AbstractNamespaces.ql @@ -14,4 +14,4 @@ where n.fromSource() and c = n.getMetrics().getAbstractness() and c > 0.2 -select n as Namespace, c as Abstractness order by Abstractness desc +select n as namespace, c as abstractness order by abstractness desc diff --git a/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql index c71f654f275..cd60b46594a 100644 --- a/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/ConcreteNamespaces.ql @@ -13,4 +13,4 @@ where n.fromSource() and c = n.getMetrics().getAbstractness() and c = 0 -select n as Namespace, c as Abstractness order by Abstractness desc +select n as namespace, c as abstractness order by abstractness desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql index 89dae1a3404..4b89e3710ce 100644 --- a/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/HighAfferentCouplingNamespaces.ql @@ -15,4 +15,4 @@ where n.fromSource() and c = n.getMetrics().getAfferentCoupling() and c > 20 -select n as Namespace, c as AfferentCoupling order by AfferentCoupling desc +select n as namespace, c as afferentCoupling order by afferentCoupling desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql index ce5553749d2..f97bb3e4f5c 100644 --- a/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/HighDistanceFromMainLineNamespaces.ql @@ -15,4 +15,4 @@ where n.fromSource() and c = n.getMetrics().getDistanceFromMain() and c > 0.7 -select n as Namespace, c as DistanceFromMainline order by DistanceFromMainline desc +select n as namespace, c as distanceFromMainline order by distanceFromMainline desc diff --git a/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql index 1de659f1384..35ebe468be6 100644 --- a/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/HighEfferentCouplingNamespaces.ql @@ -15,4 +15,4 @@ where n.fromSource() and c = n.getMetrics().getEfferentCoupling() and c > 20 -select n as Namespace, c as EfferentCoupling order by EfferentCoupling desc +select n as namespace, c as efferentCoupling order by efferentCoupling desc diff --git a/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql index 3fd17ee4066..37c1b698c56 100644 --- a/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/StableNamespaces.ql @@ -14,4 +14,4 @@ where n.fromSource() and c = n.getMetrics().getInstability() and c < 0.2 -select n as Namespace, c as Instability order by Instability desc +select n as namespace, c as instability order by instability desc diff --git a/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql index 8e150d62f03..8dbaa5f1cc7 100644 --- a/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql +++ b/cpp/ql/src/Metrics/Namespaces/UnstableNamespaces.ql @@ -14,4 +14,4 @@ where n.fromSource() and c = n.getMetrics().getInstability() and c > 0.8 -select n as Package, c as Instability order by Instability desc +select n as package, c as instability order by instability desc diff --git a/cpp/ql/src/PointsTo/TaintedFormatStrings.ql b/cpp/ql/src/PointsTo/TaintedFormatStrings.ql index 1b671697573..929833f52a0 100644 --- a/cpp/ql/src/PointsTo/TaintedFormatStrings.ql +++ b/cpp/ql/src/PointsTo/TaintedFormatStrings.ql @@ -119,4 +119,4 @@ predicate potentialViolation(InputBuffer source, FormatBuffer dest) { from InputBuffer source, FormatBuffer dest where potentialViolation(source, dest) -select dest.getFile() as File, dest as FormatString +select dest.getFile() as file, dest as formatString diff --git a/csharp/ql/lib/change-notes/2022-09-12-uppercase.md b/csharp/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/csharp/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/csharp/ql/lib/semmle/code/cil/CallableReturns.qll b/csharp/ql/lib/semmle/code/cil/CallableReturns.qll index 6a86ef72170..4cd46c10941 100644 --- a/csharp/ql/lib/semmle/code/cil/CallableReturns.qll +++ b/csharp/ql/lib/semmle/code/cil/CallableReturns.qll @@ -56,7 +56,7 @@ private predicate alwaysNotNullVariableUpdate(VariableUpdate vu) { /** Holds if expression `expr` always evaluates to non-null. */ private predicate alwaysNotNullExpr(Expr expr) { - expr instanceof Opcodes::Newobj + expr instanceof Opcodes::NewObj or expr instanceof Literal and not expr instanceof NullLiteral or diff --git a/csharp/ql/lib/semmle/code/cil/Instructions.qll b/csharp/ql/lib/semmle/code/cil/Instructions.qll index 91bc1f37ea6..7d425594b8d 100644 --- a/csharp/ql/lib/semmle/code/cil/Instructions.qll +++ b/csharp/ql/lib/semmle/code/cil/Instructions.qll @@ -766,7 +766,7 @@ module Opcodes { } /** A `newobj` instruction. */ - class Newobj extends Call, @cil_newobj { + class NewObj extends Call, @cil_newobj { override string getOpcodeName() { result = "newobj" } override int getPushCount() { result = 1 } @@ -788,6 +788,9 @@ module Opcodes { } } + /** DEPRECATED: Alias for NewObj */ + deprecated class Newobj = NewObj; + /** An `initobj` instruction. */ class Initobj extends Instruction, @cil_initobj { override string getOpcodeName() { result = "initobj" } @@ -847,10 +850,13 @@ module Opcodes { } /** A `rethrow` instruction. */ - class Rethrow extends Throw, @cil_rethrow { + class ReThrow extends Throw, @cil_rethrow { override string getOpcodeName() { result = "rethrow" } } + /** DEPRECATED: Alias for ReThrow */ + deprecated class Rethrow = ReThrow; + /** A `ldlen` instruction. */ class Ldlen extends UnaryExpr, @cil_ldlen { override string getOpcodeName() { result = "ldlen" } diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/JsonWebTokenHandlerLib.qll b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/JsonWebTokenHandlerLib.qll new file mode 100644 index 00000000000..bf9df3e5184 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/JsonWebTokenHandlerLib.qll @@ -0,0 +1,259 @@ +import csharp +import DataFlow + +/** + * A sensitive property for `TokenValidationParameters` that updates the underlying value. + */ +class TokenValidationParametersPropertySensitiveValidation extends Property { + TokenValidationParametersPropertySensitiveValidation() { + exists(Class c | + c.hasQualifiedName("Microsoft.IdentityModel.Tokens.TokenValidationParameters") + | + c.getAProperty() = this and + this.getName() in [ + "ValidateIssuer", "ValidateAudience", "ValidateLifetime", "RequireExpirationTime", + "RequireAudience" + ] + ) + } +} + +/** + * A dataflow from a `false` value to a write sensitive property for `TokenValidationParameters`. + */ +class FalseValueFlowsToTokenValidationParametersPropertyWriteToBypassValidation extends DataFlow::Configuration { + FalseValueFlowsToTokenValidationParametersPropertyWriteToBypassValidation() { + this = "FalseValueFlowsToTokenValidationParametersPropertyWriteToBypassValidation" + } + + override predicate isSource(DataFlow::Node source) { + source.asExpr().getValue() = "false" and + source.asExpr().getType() instanceof BoolType + } + + override predicate isSink(DataFlow::Node sink) { + sink.asExpr() = any(TokenValidationParametersPropertySensitiveValidation p).getAnAssignedValue() + } +} + +/** + * Holds if `assemblyName` is older than version `ver` + */ +bindingset[ver] +predicate isAssemblyOlderVersion(string assemblyName, string ver) { + exists(Assembly a | + a.getName() = assemblyName and + a.getVersion().isEarlierThan(ver) + ) +} + +/** + * A method `ValidateToken` for `Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler` or other Token handler that shares the same behavior characteristics + */ +class JsonWebTokenHandlerValidateTokenMethod extends Method { + JsonWebTokenHandlerValidateTokenMethod() { + this.hasQualifiedName("Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken") or + this.hasQualifiedName("Microsoft.AzureAD.DeviceIdentification.Common.Tokens.JwtValidator.ValidateEncryptedToken") + } +} + +/** + * A Call to `Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken` + */ +class JsonWebTokenHandlerValidateTokenCall extends MethodCall { + JsonWebTokenHandlerValidateTokenCall() { + this.getTarget() instanceof JsonWebTokenHandlerValidateTokenMethod + } +} + +/** + * A read access for properties `IsValid` or `Exception` for `Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken` + */ +private class TokenValidationResultIsValidCall extends PropertyRead { + TokenValidationResultIsValidCall() { + exists(Property p | p.getAnAccess() = this | + p.hasName("IsValid") or + p.hasName("Exception") + ) + } +} + +/** + * Dataflow from the output of `Microsoft.IdentityModel.JsonWebTokens.JsonWebTokenHandler.ValidateToken` call to access the `IsValid` or `Exception` property + */ +private class FlowsToTokenValidationResultIsValidCall extends DataFlow::Configuration { + FlowsToTokenValidationResultIsValidCall() { this = "FlowsToTokenValidationResultIsValidCall" } + + override predicate isSource(DataFlow::Node source) { + source.asExpr() instanceof JsonWebTokenHandlerValidateTokenCall + } + + override predicate isSink(DataFlow::Node sink) { + exists(TokenValidationResultIsValidCall call | sink.asExpr() = call.getQualifier()) + } +} + +/** + * A security-sensitive property for `Microsoft.IdentityModel.Tokens.TokenValidationParameters` + */ +class TokenValidationParametersProperty extends Property { + TokenValidationParametersProperty() { + exists(Class c | + c.hasQualifiedName("Microsoft.IdentityModel.Tokens.TokenValidationParameters") + | + c.getAProperty() = this and + this.getName() in [ + "SignatureValidator", "TokenReplayValidator", "AlgorithmValidator", "AudienceValidator", + "IssuerSigningKeyValidator", "LifetimeValidator" + ] + ) + } +} + +/** + * Holds if the callable has a return statement and it always returns true for all such statements + */ +predicate callableHasAReturnStmtAndAlwaysReturnsTrue(Callable c) { + c.getReturnType() instanceof BoolType and + not callableMayThrowException(c) and + forall(ReturnStmt rs | rs.getEnclosingCallable() = c | + rs.getNumberOfChildren() = 1 and + isExpressionAlwaysTrue(rs.getChildExpr(0)) + ) and + exists(ReturnStmt rs | rs.getEnclosingCallable() = c) +} + +/** + * Holds if the lambda expression `le` always returns true + */ +predicate lambdaExprReturnsOnlyLiteralTrue(AnonymousFunctionExpr le) { + le.getExpressionBody().(BoolLiteral).getBoolValue() = true + or + // special scenarios where the expression is not a `BoolLiteral`, but it will evaluatue to `true` + exists(Expr e | le.getExpressionBody() = e | + not e instanceof Call and + not e instanceof Literal and + e.getType() instanceof BoolType and + e.getValue() = "true" + ) +} + +class CallableAlwaysReturnsTrue extends Callable { + CallableAlwaysReturnsTrue() { + callableHasAReturnStmtAndAlwaysReturnsTrue(this) + or + lambdaExprReturnsOnlyLiteralTrue(this) + or + exists(AnonymousFunctionExpr le, Call call, Callable callable | this = le | + callable.getACall() = call and + call = le.getExpressionBody() and + callableHasAReturnStmtAndAlwaysReturnsTrue(callable) + ) + } +} + +/** + * Holds if any exception being thrown by the callable is of type `System.ArgumentNullException` + * It will also hold if no exceptions are thrown by the callable + */ +predicate callableOnlyThrowsArgumentNullException(Callable c) { + forall(ThrowElement thre | c = thre.getEnclosingCallable() | + thre.getThrownExceptionType().hasQualifiedName("System.ArgumentNullException") + ) +} + +/** + * A specialization of `CallableAlwaysReturnsTrue` that takes into consideration exceptions being thrown for higher precision. + */ +class CallableAlwaysReturnsTrueHigherPrecision extends CallableAlwaysReturnsTrue { + CallableAlwaysReturnsTrueHigherPrecision() { + callableOnlyThrowsArgumentNullException(this) and + ( + forall(Call call, Callable callable | call.getEnclosingCallable() = this | + callable.getACall() = call and + callable instanceof CallableAlwaysReturnsTrueHigherPrecision + ) + or + exists(AnonymousFunctionExpr le, Call call, CallableAlwaysReturnsTrueHigherPrecision cat | + this = le + | + le.canReturn(call) and + cat.getACall() = call + ) + or + exists(LambdaExpr le | le = this | + le.getBody() instanceof CallableAlwaysReturnsTrueHigherPrecision + ) + ) + } +} + +/** + * A callable that returns a `string` and has a `string` as 1st argument + */ +private class CallableReturnsStringAndArg0IsString extends Callable { + CallableReturnsStringAndArg0IsString() { + this.getReturnType() instanceof StringType and + this.getParameter(0).getType() instanceof StringType + } +} + +/** + * A Callable that always return the 1st argument, both of `string` type + */ +class CallableAlwaysReturnsParameter0 extends CallableReturnsStringAndArg0IsString { + CallableAlwaysReturnsParameter0() { + forex(Expr ret | this.canReturn(ret) | + ret = this.getParameter(0).getAnAccess() + or + exists(CallableAlwaysReturnsParameter0 c | + ret = c.getACall() and + ret.(Call).getArgument(0) = this.getParameter(0).getAnAccess() + ) + ) + } +} + +/** + * A Callable that always return the 1st argument, both of `string` type. Higher precision + */ +class CallableAlwaysReturnsParameter0MayThrowExceptions extends CallableReturnsStringAndArg0IsString { + CallableAlwaysReturnsParameter0MayThrowExceptions() { + forex(Expr ret | this.canReturn(ret) | + ret = this.getParameter(0).getAnAccess() + or + exists(CallableAlwaysReturnsParameter0MayThrowExceptions c | + ret = c.getACall() and + ret.(Call).getArgument(0) = this.getParameter(0).getAnAccess() + ) + ) + } +} + +/** + * Hold if the `Expr` e is a `BoolLiteral` with value true, + * the expression has a predictable value == `true`, + * or if it is a `ConditionalExpr` where the `then` and `else` expressions meet `isExpressionAlwaysTrue` criteria + */ +predicate isExpressionAlwaysTrue(Expr e) { + e.(BoolLiteral).getBoolValue() = true + or + e.getValue() = "true" + or + e instanceof ConditionalExpr and + isExpressionAlwaysTrue(e.(ConditionalExpr).getThen()) and + isExpressionAlwaysTrue(e.(ConditionalExpr).getElse()) + or + exists(Callable callable | + callableHasAReturnStmtAndAlwaysReturnsTrue(callable) and + callable.getACall() = e + ) +} + +/** + * Holds if the `Callable` c throws any exception other than `ThrowsArgumentNullException` + */ +predicate callableMayThrowException(Callable c) { + exists(ThrowStmt thre | c = thre.getEnclosingCallable()) and + not callableOnlyThrowsArgumentNullException(c) +} diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-bad.cs b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-bad.cs new file mode 100644 index 00000000000..2eda6821019 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-bad.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.IdentityModel.Tokens; +class TestClass +{ + public void TestMethod() + { + TokenValidationParameters parameters = new TokenValidationParameters(); + parameters.AudienceValidator = (audiences, token, tvp) => { return true; }; + } +} \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-good.cs b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-good.cs new file mode 100644 index 00000000000..28ba1d6f94e --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true-good.cs @@ -0,0 +1,17 @@ +using System; +using Microsoft.IdentityModel.Tokens; +class TestClass +{ + public void TestMethod() + { + TokenValidationParameters parameters = new TokenValidationParameters(); + parameters.AudienceValidator = (audiences, token, tvp) => + { + // Implement your own custom audience validation + if (PerformCustomAudienceValidation(audiences, token)) + return true; + else + return false; + }; + } +} \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qhelp b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qhelp new file mode 100644 index 00000000000..8c4f8dff229 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qhelp @@ -0,0 +1,28 @@ + + + +

By setting critical TokenValidationParameter validation delegates to always return true, important authentication safeguards are disabled. Disabling safeguards can lead to incorrect validation of tokens from any issuer or expired tokens.

+ +
+ +

Improve the logic of the delegate so not all code paths return true, which effectively disables that type of validation; or throw SecurityTokenInvalidAudienceException or SecurityTokenInvalidLifetimeException in failure cases when you want to fail validation and have other cases pass by returning true. +

+
+ + +

This example delegates AudienceValidator to a callable that always returns true.

+ + +

To fix it, use a callable that performs a validation, and fails when appropriate.

+ + +
+ + + +
  • azure-activedirectory-identitymodel-extensions-for-dotnet ValidatingTokens wiki
  • + +
    +
    \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.ql b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.ql new file mode 100644 index 00000000000..57561944718 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.ql @@ -0,0 +1,21 @@ +/** + * @name Delegated security sensitive validations for JsonWebTokenHandler always return true, medium precision + * @description Security sensitive validations for `JsonWebTokenHandler` are being delegated to a function that seems to always return true. + * Higher precision version checks for exception throws, so less false positives are expected. + * @kind problem + * @tags security + * JsonWebTokenHandler + * manual-verification-required + * @id cs/json-webtoken-handler/delegated-security-validations-always-return-true + * @problem.severity error + * @precision high + */ + +import csharp +import DataFlow +import JsonWebTokenHandlerLib + +from TokenValidationParametersProperty p, CallableAlwaysReturnsTrueHigherPrecision e +where e = p.getAnAssignedValue() +select e, "JsonWebTokenHandler security-sensitive property $@ is being delegated to $@.", p, + p.getQualifiedName().toString(), e, "a callable that always returns \"true\"" diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-bad.cs b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-bad.cs new file mode 100644 index 00000000000..81df44fea9a --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-bad.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.IdentityModel.Tokens; +class TestClass +{ + public void TestMethod() + { + TokenValidationParameters parameters = new TokenValidationParameters(); + parameters.RequireExpirationTime = false; + parameters.ValidateAudience = false; + parameters.ValidateIssuer = false; + parameters.ValidateLifetime = false; + } +} \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-good.cs b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-good.cs new file mode 100644 index 00000000000..e2f74c0653d --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-good.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.IdentityModel.Tokens; +class TestClass +{ + public void TestMethod() + { + TokenValidationParameters parameters = new TokenValidationParameters(); + parameters.RequireExpirationTime = true; + parameters.ValidateAudience = true; + parameters.ValidateIssuer = true; + parameters.ValidateLifetime = true; + } +} \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qhelp b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qhelp new file mode 100644 index 00000000000..5c027ac3148 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qhelp @@ -0,0 +1,27 @@ + + + +

    Token validation checks ensure that while validating tokens, all aspects are analyzed and verified. Turning off validation can lead to security holes by allowing untrusted tokens to make it through validation.

    + +
    + +

    Set Microsoft.IdentityModel.Tokens.TokenValidationParameters properties RequireExpirationTime, ValidateAudience, ValidateIssuer, or ValidateLifetime to true. Or, remove the assignment to false because the default value is true.

    +
    + + +

    This example disabled the validation.

    + + +

    To fix it, do not disable the validations or use the default value.

    + + +
    + + + +
  • azure-activedirectory-identitymodel-extensions-for-dotnet ValidatingTokens wiki
  • + +
    +
    \ No newline at end of file diff --git a/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.ql b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.ql new file mode 100644 index 00000000000..cfc745e5314 --- /dev/null +++ b/csharp/ql/src/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.ql @@ -0,0 +1,24 @@ +/** + * @name Security sensitive JsonWebTokenHandler validations are disabled + * @description Check if secruity sensitive token validations for `JsonWebTokenHandler` are being disabled. + * @kind problem + * @tags security + * JsonWebTokenHandler + * manual-verification-required + * @id cs/json-webtoken-handler/security-validations-disabled + * @problem.severity error + * @precision high + */ + +import csharp +import JsonWebTokenHandlerLib + +from + FalseValueFlowsToTokenValidationParametersPropertyWriteToBypassValidation config, + DataFlow::Node source, DataFlow::Node sink, + TokenValidationParametersPropertySensitiveValidation pw +where + config.hasFlow(source, sink) and + sink.asExpr() = pw.getAnAssignedValue() +select sink, "The security sensitive property $@ is being disabled by the following value: $@.", pw, + pw.getQualifiedName().toString(), source, "false" diff --git a/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll b/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll index 0fedd38bfbd..6fca9e3b974 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/AliasedSSAStub.qll @@ -6,7 +6,7 @@ private import IRFunctionBase private import TInstruction -module SSA { +module Ssa { class MemoryLocation = boolean; predicate hasPhiInstruction(TRawInstruction blockStartInstr, MemoryLocation memoryLocation) { @@ -17,3 +17,6 @@ module SSA { predicate hasUnreachedInstruction(IRFunctionBase irFunc) { none() } } + +/** DEPRECATED: Alias for Ssa */ +deprecated module SSA = Ssa; diff --git a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll index b30372a791b..5564a16f215 100644 --- a/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/internal/TInstruction.qll @@ -20,24 +20,24 @@ newtype TInstruction = IRConstruction::Raw::hasInstruction(tag1, tag2) } or TUnaliasedSsaPhiInstruction( - TRawInstruction blockStartInstr, UnaliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation ) { - UnaliasedSsa::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + UnaliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation) } or TUnaliasedSsaChiInstruction(TRawInstruction primaryInstruction) { none() } or TUnaliasedSsaUnreachedInstruction(IRFunctionBase irFunc) { - UnaliasedSsa::SSA::hasUnreachedInstruction(irFunc) + UnaliasedSsa::Ssa::hasUnreachedInstruction(irFunc) } or TAliasedSsaPhiInstruction( - TRawInstruction blockStartInstr, AliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation ) { - AliasedSsa::SSA::hasPhiInstruction(blockStartInstr, memoryLocation) + AliasedSsa::Ssa::hasPhiInstruction(blockStartInstr, memoryLocation) } or TAliasedSsaChiInstruction(TRawInstruction primaryInstruction) { - AliasedSsa::SSA::hasChiInstruction(primaryInstruction) + AliasedSsa::Ssa::hasChiInstruction(primaryInstruction) } or TAliasedSsaUnreachedInstruction(IRFunctionBase irFunc) { - AliasedSsa::SSA::hasUnreachedInstruction(irFunc) + AliasedSsa::Ssa::hasUnreachedInstruction(irFunc) } /** @@ -50,7 +50,7 @@ module UnaliasedSsaInstructions { class TPhiInstruction = TUnaliasedSsaPhiInstruction; TPhiInstruction phiInstruction( - TRawInstruction blockStartInstr, UnaliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, UnaliasedSsa::Ssa::MemoryLocation memoryLocation ) { result = TUnaliasedSsaPhiInstruction(blockStartInstr, memoryLocation) } @@ -83,7 +83,7 @@ module AliasedSsaInstructions { class TPhiInstruction = TAliasedSsaPhiInstruction or TUnaliasedSsaPhiInstruction; TPhiInstruction phiInstruction( - TRawInstruction blockStartInstr, AliasedSsa::SSA::MemoryLocation memoryLocation + TRawInstruction blockStartInstr, AliasedSsa::Ssa::MemoryLocation memoryLocation ) { result = TAliasedSsaPhiInstruction(blockStartInstr, memoryLocation) } diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll index 1c75529be00..873a3c635f8 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConsistency.qll @@ -1,2 +1,2 @@ -private import SSAConstruction as SSA -import SSA::SsaConsistency +private import SSAConstruction as Ssa +import Ssa::SsaConsistency diff --git a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll index 901735069c0..21c03e176a5 100644 --- a/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll +++ b/csharp/ql/src/experimental/ir/implementation/unaliased_ssa/internal/SSAConstruction.qll @@ -1135,7 +1135,7 @@ deprecated module SSAConsistency = SsaConsistency; * These predicates are all just aliases for predicates defined in the `Cached` module. This ensures * that all of SSA construction will be evaluated in the same stage. */ -module SSA { +module Ssa { class MemoryLocation = Alias::MemoryLocation; predicate hasPhiInstruction = Cached::hasPhiInstructionCached/2; @@ -1144,3 +1144,6 @@ module SSA { predicate hasUnreachedInstruction = Cached::hasUnreachedInstructionCached/1; } + +/** DEPRECATED: Alias for Ssa */ +deprecated module SSA = Ssa; diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.expected b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.expected new file mode 100644 index 00000000000..dc224c9586e --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.expected @@ -0,0 +1,7 @@ +| delegation-test.cs:101:63:101:186 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:54:34:54:50 | LifetimeValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.LifetimeValidator | delegation-test.cs:101:63:101:186 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:102:63:102:178 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:102:63:102:178 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:115:63:115:190 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:115:63:115:190 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:116:63:116:180 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:116:63:116:180 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:117:63:117:217 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:117:63:117:217 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:118:63:118:248 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:118:63:118:248 | (...) => ... | a callable that always returns "true" | +| delegation-test.cs:119:63:119:177 | (...) => ... | JsonWebTokenHandler security-sensitive property $@ is being delegated to $@. | stubs.cs:55:34:55:50 | AudienceValidator | Microsoft.IdentityModel.Tokens.TokenValidationParameters.AudienceValidator | delegation-test.cs:119:63:119:177 | (...) => ... | a callable that always returns "true" | diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qlref b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qlref new file mode 100644 index 00000000000..527ea925973 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.qlref @@ -0,0 +1 @@ +experimental/Security Features/JsonWebTokenHandler/delegated-security-validations-always-return-true.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegation-test.cs b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegation-test.cs new file mode 100644 index 00000000000..01af41c4b0c --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/delegation-test.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.JsonWebTokens; + +namespace JsonWebTokenHandlerTest +{ + public class JsonWebTokenHandler_00 + { + public static object ThrowIfNull(string name, object value) + { + if (value == null) + { + throw new System.ArgumentNullException(name); + } + return value; + } + + private static bool MayThrowException(SecurityToken token) + { + if (token.Id == null) + { + throw new Exception("foobar"); + } + return true; + } + + private static void DoesNotThrowException(SecurityToken token) + { + int x = 0; + } + + private static bool ValidateLifetime_FP01( + SecurityToken token, + TokenValidationParameters validationParameters) + { + if (token == null) + { + throw new System.ArgumentNullException("token"); + } + + MayThrowException(token); + + return true; + } + + private static bool ValidateLifetime_P01( + SecurityToken token, + TokenValidationParameters validationParameters) + { + if (token == null) + { + throw new System.ArgumentNullException("token"); + } + + DoesNotThrowException(token); + + return true; + } + + + internal static bool ValidateLifetimeAlwaysTrue( + SecurityToken token, + TokenValidationParameters validationParameters) + { + if (token is null) + { + return true; + } + return true; + } + + internal static bool ValidateLifetime( + string token, + TokenValidationParameters validationParameters) + { + if (token is null) + { + return false; + } + return true; + } + + public void TestCase01() + { + TokenValidationParameters tokenValidationParamsBaseline = new TokenValidationParameters + { + ClockSkew = TimeSpan.FromMinutes(5), + ValidateActor = true, + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + RequireExpirationTime = true, + ValidateTokenReplay = true, + RequireSignedTokens = true, + RequireAudience = true, + SaveSigninToken = true + }; + + tokenValidationParamsBaseline.LifetimeValidator = (notBefore, expires, securityToken, validationParameters) => ValidateLifetimeAlwaysTrue(securityToken, validationParameters); // BUG delegated-security-validations-always-return-true + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => true; // BUG delegated-security-validations-always-return-true + tokenValidationParamsBaseline.TokenReplayValidator = (DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters) => // GOOD + { + if (securityToken is null) + { + return false; + } + return true; + }; + + tokenValidationParamsBaseline.LifetimeValidator = (notBefore, expires, securityToken, validationParameters) => ValidateLifetime02(securityToken, validationParameters); // GOOD + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => {return securityToken is null?false:true; }; // GOOD + + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => { return true; }; // BUG + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => !false ; // BUG + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => { return securityToken is null?true:true; }; // BUG + tokenValidationParamsBaseline.AudienceValidator = (IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters) => { return ValidateLifetimeAlwaysTrue(securityToken, validationParameters);}; //BUG + tokenValidationParamsBaseline.AudienceValidator = (audiences, securityToken, validationParameters) => ValidateLifetimeAlwaysTrue(securityToken, validationParameters); //BUG + + } + + internal static bool ValidateLifetime02( + SecurityToken token, + TokenValidationParameters validationParameters) + { + return token is null?false:true; + } + + internal static bool ValidateLifetimeAlwaysTrue02( + SecurityToken token, + TokenValidationParameters validationParameters) + { + return !false; + } + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-test.cs b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-test.cs new file mode 100644 index 00000000000..505aba41416 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled-test.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using Microsoft.IdentityModel.Tokens; + +namespace JsonWebTokenHandlerTest +{ + public class JsonWebTokenHandler_class01 + { + public void TestCase01() + { + TokenValidationParameters tokenValidationParamsBaseline = new TokenValidationParameters + { + ClockSkew = TimeSpan.FromMinutes(5), + ValidateActor = true, + ValidateIssuerSigningKey = true, + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + RequireExpirationTime = true, + ValidateTokenReplay = true, + RequireSignedTokens = true, + RequireAudience = true, + SaveSigninToken = true + }; + + TokenValidationParameters tokenValidationParams = new TokenValidationParameters + { + ClockSkew = TimeSpan.FromMinutes(5), + ValidateActor = false, + ValidateIssuerSigningKey = false, + ValidateIssuer = false, // BUG + ValidateAudience = false, // BUG + ValidateLifetime = false, // BUG + RequireExpirationTime = false, // BUG + ValidateTokenReplay = false, + RequireSignedTokens = false, + RequireAudience = false, // BUG + SaveSigninToken = false + }; + } + + } +} \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.expected b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.expected new file mode 100644 index 00000000000..4a0a5afce6f --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.expected @@ -0,0 +1,5 @@ +| security-validation-disabled-test.cs:31:34:31:38 | false | The security sensitive property $@ is being disabled by the following value: $@. | stubs.cs:43:21:43:34 | ValidateIssuer | Microsoft.IdentityModel.Tokens.TokenValidationParameters.ValidateIssuer | security-validation-disabled-test.cs:31:34:31:38 | false | false | +| security-validation-disabled-test.cs:32:36:32:40 | false | The security sensitive property $@ is being disabled by the following value: $@. | stubs.cs:44:21:44:36 | ValidateAudience | Microsoft.IdentityModel.Tokens.TokenValidationParameters.ValidateAudience | security-validation-disabled-test.cs:32:36:32:40 | false | false | +| security-validation-disabled-test.cs:33:36:33:40 | false | The security sensitive property $@ is being disabled by the following value: $@. | stubs.cs:45:21:45:36 | ValidateLifetime | Microsoft.IdentityModel.Tokens.TokenValidationParameters.ValidateLifetime | security-validation-disabled-test.cs:33:36:33:40 | false | false | +| security-validation-disabled-test.cs:34:41:34:45 | false | The security sensitive property $@ is being disabled by the following value: $@. | stubs.cs:51:21:51:41 | RequireExpirationTime | Microsoft.IdentityModel.Tokens.TokenValidationParameters.RequireExpirationTime | security-validation-disabled-test.cs:34:41:34:45 | false | false | +| security-validation-disabled-test.cs:37:35:37:39 | false | The security sensitive property $@ is being disabled by the following value: $@. | stubs.cs:50:21:50:35 | RequireAudience | Microsoft.IdentityModel.Tokens.TokenValidationParameters.RequireAudience | security-validation-disabled-test.cs:37:35:37:39 | false | false | diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qlref b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qlref new file mode 100644 index 00000000000..ee07957fa06 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.qlref @@ -0,0 +1 @@ +experimental/Security Features/JsonWebTokenHandler/security-validation-disabled.ql \ No newline at end of file diff --git a/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/stubs.cs b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/stubs.cs new file mode 100644 index 00000000000..1d0e0de00a3 --- /dev/null +++ b/csharp/ql/test/experimental/Security Features/JsonWebTokenHandler/stubs.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; + +namespace Microsoft.IdentityModel +{ + +} + +namespace Microsoft.IdentityModel.Tokens +{ + public abstract class SecurityToken + { + protected SecurityToken() { } + public string Id { get; } + public string Issuer { get; } + public DateTime ValidFrom { get; } + public DateTime ValidTo { get; } + } + + public abstract class TokenHandler + { + public static readonly int DefaultTokenLifetimeInMinutes; + + protected TokenHandler() { } + + public virtual int MaximumTokenSizeInBytes { get; set; } + public bool SetDefaultTimesOnTokenCreation { get; set; } + public int TokenLifetimeInMinutes { get; set; } + } + + public delegate bool LifetimeValidator(DateTime? notBefore, DateTime? expires, SecurityToken securityToken, TokenValidationParameters validationParameters); + public delegate bool AudienceValidator(IEnumerable audiences, SecurityToken securityToken, TokenValidationParameters validationParameters); + public delegate bool TokenReplayValidator(DateTime? expirationTime, string securityToken, TokenValidationParameters validationParameters); + public delegate string IssuerValidator(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters); + + public class TokenValidationParameters + { + public const int DefaultMaximumTokenSizeInBytes = 256000; + public static readonly string DefaultAuthenticationType; + public static readonly TimeSpan DefaultClockSkew; + public TimeSpan ClockSkew { get; set; } + public bool SaveSigninToken { get; set; } + public bool ValidateIssuer { get; set; } + public bool ValidateAudience { get; set; } + public bool ValidateLifetime { get; set; } + public bool ValidateIssuerSigningKey { get; set; } + public bool ValidateTokenReplay { get; set; } + public bool ValidateActor { get; set; } + public bool RequireSignedTokens { get; set; } + public bool RequireAudience { get; set; } + public bool RequireExpirationTime { get; set; } + + // Delegation + public LifetimeValidator LifetimeValidator { get; set; } + public AudienceValidator AudienceValidator { get; set; } + public TokenReplayValidator TokenReplayValidator { get; set; } + public IssuerValidator IssuerValidator { get; set; } + + /* + public TokenValidationParameters() { } + public SignatureValidator SignatureValidator { get; set; } + public SecurityKey TokenDecryptionKey { get; set; } + public TokenDecryptionKeyResolver TokenDecryptionKeyResolver { get; set; } + public IEnumerable TokenDecryptionKeys { get; set; } + public TokenReader TokenReader { get; set; } + public ITokenReplayCache TokenReplayCache { get; set; } + public Func RoleClaimTypeRetriever { get; set; } + public string ValidAudience { get; set; } + public IEnumerable ValidAudiences { get; set; } + public string ValidIssuer { get; set; } + public IEnumerable ValidIssuers { get; set; } + public TokenValidationParameters ActorValidationParameters { get; set; } + public AudienceValidator AudienceValidator { get; set; } + public string AuthenticationType { get; set; } + public CryptoProviderFactory CryptoProviderFactory { get; set; } + public IssuerSigningKeyValidator IssuerSigningKeyValidator { get; set; } + public SecurityKey IssuerSigningKey { get; set; } + public IEnumerable IssuerSigningKeys { get; set; } + public IssuerValidator IssuerValidator { get; set; } + public string NameClaimType { get; set; } + public string RoleClaimType { get; set; } + public Func NameClaimTypeRetriever { get; set; } + public IDictionary PropertyBag { get; set; } + public IssuerSigningKeyResolver IssuerSigningKeyResolver { get; set; } + public IEnumerable ValidTypes { get; set; } + public virtual TokenValidationParameters Clone(); + public virtual string CreateClaimsIdentity(SecurityToken securityToken, string issuer); + */ + } + +} + +namespace Microsoft.IdentityModel.JsonWebTokens +{ + public class JsonWebTokenHandler : Microsoft.IdentityModel.Tokens.TokenHandler + { + public virtual TokenValidationResult ValidateToken(string token, Microsoft.IdentityModel.Tokens.TokenValidationParameters validationParameters) + { + return new TokenValidationResult() { IsValid = true, Exception = null, Issuer = "test" }; + } + } + + public class TokenValidationResult + { + public TokenValidationResult() { } + + public Exception Exception { get; set; } + public string Issuer { get; set; } + public bool IsValid { get; set; } + public Microsoft.IdentityModel.Tokens.SecurityToken SecurityToken { get; set; } + public string ClaimsIdentity { get; set; } + } + + +} diff --git a/docs/codeql/codeql-cli/about-codeql-workspaces.rst b/docs/codeql/codeql-cli/about-codeql-workspaces.rst index 3215c44042e..ba7baaf7361 100644 --- a/docs/codeql/codeql-cli/about-codeql-workspaces.rst +++ b/docs/codeql/codeql-cli/about-codeql-workspaces.rst @@ -1,6 +1,6 @@ .. _about-codeql-workspaces: -About CodeQL Workspaces +About CodeQL workspaces ======================= .. include:: ../reusables/beta-note-package-management.rst diff --git a/docs/codeql/codeql-cli/about-ql-packs.rst b/docs/codeql/codeql-cli/about-ql-packs.rst new file mode 100644 index 00000000000..2f8d4d9229d --- /dev/null +++ b/docs/codeql/codeql-cli/about-ql-packs.rst @@ -0,0 +1,12 @@ +.. _about-ql-packs: + +About QL packs +============== + +This page has been moved to ":doc:`About CodeQL packs `." + + +.. toctree:: + :hidden: + + about-ql-packs diff --git a/docs/codeql/codeql-cli/creating-codeql-databases.rst b/docs/codeql/codeql-cli/creating-codeql-databases.rst index 62ed161e9b9..65afbca40c1 100644 --- a/docs/codeql/codeql-cli/creating-codeql-databases.rst +++ b/docs/codeql/codeql-cli/creating-codeql-databases.rst @@ -5,11 +5,17 @@ Creating CodeQL databases Before you analyze your code using CodeQL, you need to create a CodeQL database containing all the data required to run queries on your code. +You can create CodeQL databases yourself using the CodeQL CLI, +or download them from GitHub.com. CodeQL analysis relies on extracting relational data from your code, and using it to build a :ref:`CodeQL database `. CodeQL databases contain all of the important information about a codebase, which can -be analyzed by executing CodeQL queries against it. +be analyzed by executing CodeQL queries against it. GitHub creates and +stores CodeQL databases for a large number of open-source projects. For more information, +see ":ref:`Downloading CodeQL databases from GitHub.com `." + +You can also create CodeQL databases yourself using the CodeQL CLI. Before you generate a CodeQL database, you need to: - Install and set up the CodeQL CLI. For more information, see @@ -379,24 +385,15 @@ The following example shows how you could use indirect build tracing in an Azure # `codeql database analyze` # then `codeql github upload-results` ... -Obtaining databases from LGTM.com ---------------------------------- - -`LGTM.com `__ analyzes thousands of open-source projects using -CodeQL. For each project on LGTM.com, you can download an archived CodeQL -database corresponding to the most recently analyzed revision of the code. These -databases can also be analyzed using the CodeQL CLI or used with the CodeQL -extension for Visual Studio Code. - -.. include:: ../reusables/download-lgtm-database.rst - -.. pull-quote:: - - Note - - .. include:: ../reusables/index-files-note.rst +.. _downloading-codeql-databases-from-github-com: +Downloading databases from GitHub.com +------------------------------------- +.. include:: ../reusables/download-github-database.rst + +Before running an analysis with the CodeQL CLI, you must unzip the databases. + Further reading --------------- diff --git a/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst b/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst index 0fd8e6b8fa0..b3ad59da2b5 100644 --- a/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst +++ b/docs/codeql/codeql-cli/getting-started-with-the-codeql-cli.rst @@ -47,22 +47,11 @@ Conditions `__. .. pull-quote:: Important - There are different versions of the CLI available to download, depending + There are several versions of the CLI available to download, depending on your use case: - - If you want to use the most up to date CodeQL tools and features, download the - version tagged ``latest``. - - - If you want to create CodeQL databases to upload to LGTM Enterprise, download - the version that is compatible with the relevant LGTM Enterprise version - number. Compatibility information is included in the description for each - release on the `CodeQL CLI releases page - `__ on GitHub. Using the - correct version of the CLI ensures that your CodeQL databases are - compatible with your version of LGTM Enterprise. For more information, - see `Preparing CodeQL databases to upload to LGTM - `__ - in the LGTM admin help. + * If you want to use the most up to date CodeQL tools and features, download the version tagged ``latest``. + * If you want to generate code scanning data to upload to GitHub Enterprise server, then download the version that is compatible with the CodeQL CLI used in your CI system. For more information, see "`Installing CodeQL CLI in your CI system `__" in the GitHub documentation. If you use Linux, Windows, or macOS version 10.14 ("Mojave") or earlier, simply `download the zip archive @@ -232,15 +221,7 @@ see ":doc:`About CodeQL packs `." - For the most up to date CodeQL queries, check out the ``main`` branch. This branch represents the very latest version of CodeQL's analysis. - - For the queries used in a particular LGTM Enterprise release, check out the - branch tagged with the relevant release number. For example, the branch - tagged ``v1.27.0`` corresponds to LGTM Enterprise 1.27. You must use this - version if you want to upload data to LGTM Enterprise. For further - information, see `Preparing CodeQL databases to upload to LGTM - `__ - in the LGTM admin help. - -1. Extract the zip archive +4. Extract the zip archive ~~~~~~~~~~~~~~~~~~~~~~~~~~ For Linux, Windows, and macOS users (version 10.14 "Mojave", and earlier) diff --git a/docs/codeql/codeql-for-visual-studio-code/analyzing-your-projects.rst b/docs/codeql/codeql-for-visual-studio-code/analyzing-your-projects.rst index b23ddacf157..89fe41c454e 100644 --- a/docs/codeql/codeql-for-visual-studio-code/analyzing-your-projects.rst +++ b/docs/codeql/codeql-for-visual-studio-code/analyzing-your-projects.rst @@ -14,7 +14,7 @@ To analyze a project, you need to add a :ref:`CodeQL database ` #. Open the CodeQL Databases view in the sidebar. -#. Hover over the **Databases** title bar and click the appropriate icon to add your database. You can add a database from a local ZIP archive or folder, from a public URL, or from a project slug or URL on LGTM.com. +#. Hover over the **Databases** title bar and click the appropriate icon to add your database. You can add a database from a local ZIP archive or folder, from a public URL, or from a project's URL on GitHub.com. .. image:: ../images/codeql-for-visual-studio-code/choose-database.png :width: 350 @@ -31,14 +31,14 @@ If you have a CodeQL database saved locally, as an unarchived folder or as a ZIP - To create a database with the CodeQL CLI, see ":ref:`Creating CodeQL databases `." -- .. include:: ../reusables/download-lgtm-database.rst - - To analyze a test database, add a ``.testproj`` folder to the Databases view. Test databases (that is, folders with a ``.testproj`` extension) are generated when you run regression tests on custom queries using the :ref:`CodeQL CLI `. If a query fails a regression test, you may want to analyze the test database in Visual Studio Code to debug the failure. For more information about running query tests, see ":ref:`Testing custom queries `" in the CodeQL CLI help. +.. include:: ../reusables/download-github-database.rst + Running a query ------------------------ diff --git a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst index 940a8a0c50c..8899bc97bc7 100644 --- a/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst +++ b/docs/codeql/codeql-for-visual-studio-code/customizing-settings.rst @@ -28,7 +28,7 @@ Editing settings Choosing a version of the CodeQL CLI -------------------------------------- -The CodeQL extension uses the CodeQL CLI to run commands. If you already have the CLI installed and added to your ``PATH``, the extension uses that version. This might be the case if you create your own CodeQL databases instead of downloading them from LGTM.com. Otherwise, the extension automatically manages access to the executable of the CLI for you. For more information about creating databases, see ":ref:`Creating CodeQL databases `" in the CLI help. +The CodeQL extension uses the CodeQL CLI to run commands. If you already have the CLI installed and added to your ``PATH``, the extension uses that version. This might be the case if you create your own CodeQL databases instead of downloading them from GitHub.com. Otherwise, the extension automatically manages access to the executable of the CLI for you. For more information about creating databases, see ":ref:`Creating CodeQL databases `" in the CLI help. To override the default behavior and use a different CLI, you can specify the CodeQL CLI **Executable Path**. diff --git a/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst b/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst index 1c762348a9d..5592992373b 100644 --- a/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst +++ b/docs/codeql/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code.rst @@ -28,7 +28,7 @@ Configuring access to the CodeQL CLI The extension uses the CodeQL CLI to compile and run queries. -If you already have the CLI installed and added to your ``PATH``, the extension uses that version. This might be the case if you create your own CodeQL databases instead of downloading them from LGTM.com. For more information, see ":ref:`CodeQL CLI `." +If you already have the CLI installed and added to your ``PATH``, the extension uses that version. This might be the case if you create your own CodeQL databases instead of downloading them from GitHub.com. For more information, see ":ref:`CodeQL CLI `." Otherwise, the extension automatically manages access to the executable of the CLI for you. This ensures that the CLI is compatible with the CodeQL extension. You can also check for updates with the **CodeQL: Check for CLI Updates** command. @@ -57,20 +57,6 @@ There are two ways to do this: For CLI users there is a third option: If you have followed the instructions in ":ref:`Getting started with the CodeQL CLI `" to create a CodeQL directory (for example ``codeql-home``) containing the CodeQL libraries, you can open this directory in VS Code. This also gives the extension access to the CodeQL libraries. -.. container:: toggle - - .. container:: name - - **Click to show information for LGTM Enterprise users** - - Your local version of the CodeQL queries and libraries should match your version of LGTM Enterprise. For example, if you - use LGTM Enterprise 1.27, then you should clone the ``1.27.0`` branch of the `starter workspace `__ (or the appropriate ``1.27.x`` branch, corresponding to each maintenance release). - - This ensures that the queries and libraries you write in VS Code also work in the query console on LGTM Enterprise. - - If you prefer to add the CodeQL queries and libraries to an :ref:`existing workspace ` instead of the starter workspace, then you should - clone the appropriate branch of the `CodeQL repository `__ and add it to your workspace. - .. _starter-workspace: Using the starter workspace diff --git a/docs/codeql/images/codeql-for-visual-studio-code/choose-database.png b/docs/codeql/images/codeql-for-visual-studio-code/choose-database.png index 71ca5d6b4c3..58287111cc1 100644 Binary files a/docs/codeql/images/codeql-for-visual-studio-code/choose-database.png and b/docs/codeql/images/codeql-for-visual-studio-code/choose-database.png differ diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index ea90ebbf843..68c4475aa0f 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -57,44 +57,27 @@ construction of the library path. First, determine the *query directory* of the ``.ql`` file being compiled. Starting with the directory containing the ``.ql`` file, and walking up the directory structure, each directory is checked for a -file called ``queries.xml`` or ``qlpack.yml``. The first directory +file called ``qlpack.yml`` or ``codeql-pack.yml``. The first directory where such a file is found is the query directory. If there is no such directory, the directory of the ``.ql`` file itself is the query directory. -A ``queries.xml`` file that defines a query directory must always -contain a single top-level tag named -``queries``, which has a ``language`` attribute set to the identifier -of the active database schema (for example, ````). - A ``qlpack.yml`` file defines a :ref:`CodeQL pack `. The content of a ``qlpack.yml`` file is described in the CodeQL CLI documentation. +``codeql-pack.yml`` is an alias for ``qlpack.yml``. -If both a ``queries.xml`` and a ``qlpack.yml`` exist in the same -directory, the latter takes precedence (and the former is assumed to -exist for compatibility with older tooling). - -The CodeQL CLI and newer tools based on it (such as, +The CodeQL CLI and tools based on it (such as, GitHub code scanning and the CodeQL extension for Visual Studio Code) construct a library path using CodeQL packs. For each CodeQL pack added to the library path, the CodeQL packs named in its -``libraryPathDependencies`` will be subsequently added to the library +``dependencies`` will be subsequently added to the library path, and the process continues until all packs have been resolved. The actual library path consists of the root directories of the selected CodeQL packs. This process depends on a mechanism for finding -CodeQL packs by pack name, as described in the :ref:`CodeQL CLI documentation `. +CodeQL packs by pack name and version, as described in the :ref:`CodeQL CLI documentation `. -When the query directory contains a ``queries.xml`` file but no -``qlpack.yml``, the CodeQL pack resolution behaves as if it defines a QL -pack with no name and a single library path dependency named -``legacy-libraries-LANGUAGE`` where ``LANGUAGE`` is taken from -``queries.xml``. The ``github/codeql`` repository provides packs with -names following this pattern, which themselves depend on the actual -CodeQL libraries for each language. - -When the query directory contains neither a ``queries.xml`` nor -``qlpack.yml`` file, it is considered to be a CodeQL pack with no name and +When the query directory contains neither a ``qlpack.yml`` nor +``codeql-pack.yml`` file, it is considered to be a CodeQL pack with no name and no library dependencies. This causes the library path to consist of *only* the query directory itself. This is not generally useful, but it suffices for running toy examples of QL code that don't diff --git a/docs/codeql/reusables/download-github-database.rst b/docs/codeql/reusables/download-github-database.rst new file mode 100644 index 00000000000..1156c273456 --- /dev/null +++ b/docs/codeql/reusables/download-github-database.rst @@ -0,0 +1,14 @@ +GitHub stores CodeQL databases for over 200,000 repos on GitHub.com, which you can download using the REST API. The list of repos is constantly growing and evolving to make sure that it includes the most interesting codebases for security research. + +You can check if a repository has any CodeQL databases available for download using the ``/repos///code-scanning/codeql/databases`` endpoint. +For example, to check for CodeQL databases using the `GitHub CLI `__ you would run:: + + gh api /repos///code-scanning/codeql/databases/ + +This command returns information about any CodeQL databases that are available for a repository, including the language the database represents, and when the database was last updated. If no CodeQL databases are available, the response is empty. + +When you have confirmed that a CodeQL database exists for the language you are interested in, you can download it using the following command:: + + gh api /repos///code-scanning/codeql/databases/ -H 'Accept: application/zip' > path/to/local/database.zip + +For more information, see the documentation for the `Get CodeQL database `__ endpoint in the GitHub REST API documentation. diff --git a/docs/codeql/reusables/download-lgtm-database.rst b/docs/codeql/reusables/download-lgtm-database.rst deleted file mode 100644 index 11652d6512a..00000000000 --- a/docs/codeql/reusables/download-lgtm-database.rst +++ /dev/null @@ -1,6 +0,0 @@ -To download a database from LGTM.com: - -#. Log in to `LGTM.com `__. -#. Find a project you're interested in and display the Integrations tab (for example, `Apache Kafka `__). -#. Scroll to the **CodeQL databases for local analysis** section at the bottom of the page. -#. Download databases for the languages that you want to explore. \ No newline at end of file diff --git a/docs/codeql/reusables/index-files-note.rst b/docs/codeql/reusables/index-files-note.rst deleted file mode 100644 index aad0637cbe1..00000000000 --- a/docs/codeql/reusables/index-files-note.rst +++ /dev/null @@ -1,8 +0,0 @@ -The CodeQL CLI currently extracts data from additional, external files in a -different way to the legacy QL tools. For example, when you run ``codeql database create`` -the CodeQL CLI extracts data from some relevant XML files for Java and C#, but not -for the other supported languages, such as JavaScript. This means that CodeQL databases -created using the CodeQL CLI may be slightly different from those obtained from LGTM.com or -created using the legacy QL command-line tools. As such, analysis results generated from -databases created using the CodeQL CLI may also differ from those generated from -databases obtained from elsewhere. \ No newline at end of file diff --git a/docs/ql-design-patterns.md b/docs/ql-design-patterns.md index 6984088e925..b6d7eab88fc 100644 --- a/docs/ql-design-patterns.md +++ b/docs/ql-design-patterns.md @@ -31,13 +31,9 @@ Applying the `::Range` pattern yields the following: * Extend this class to refine existing API models. If you want to model new APIs, * extend `MySpecialExpr::Range` instead. */ -class MySpecialExpr extends Expr { - MySpecialExpr::Range range; - - MySpecialExpr() { this = range } - +class MySpecialExpr extends Expr instanceof MySpecialExpr::Range { /** */ - int memberPredicate() { result = range.memberPredicate() } + int memberPredicate() { result = super.memberPredicate() } } /** Provides a class for modeling new <...> APIs. */ @@ -56,22 +52,18 @@ module MySpecialExpr { ``` Now, a concrete subclass can derive from `MySpecialExpr::Range` if it wants to extend the set of values in `MySpecialExpr`, and it will be required to implement the abstract `memberPredicate()`. Conversely, if it wants to refine `MySpecialExpr` and override `memberPredicate` for all extensions, it can do so by deriving from `MySpecialExpr` directly. -The key element of the pattern is to provide a field of type `MySpecialExpr::Range`, equating it to `this` in the characteristic predicate of `MySpecialExpr`. In member predicates, we can use either `this` or `range`, depending on which type has the API we need. - -Note that in some libraries, the `range` field is in fact called `self`. While we do recommend using `range` for consistency, the name of the field does not matter (and using `range` avoids confusion in contexts like Python analysis that has strong usage of `self`). - ### Rationale -Let's use an example from the Go libraries: https://github.com/github/codeql-go/blob/2ba9bbfd8ba1818b5ee9f6009c86a605189c9ef3/ql/src/semmle/go/Concepts.qll#L119-L157 +Let's use an example from the Python libraries: https://github.com/github/codeql/blob/46751e515c40c6b4c9b61758cc840eec1894a624/python/ql/lib/semmle/python/Concepts.qll#L601-L683 -`EscapeFunction`, as the name suggests, models various APIs that escape meta-characters. It has a member-predicate `kind()` that tells you what sort of escaping the modelled function does. For example, if the result of that predicate is `"js"`, then this means that the escaping function is meant to make things safe to embed inside JavaScript. -`EscapeFunction::Range` is subclassed to model various APIs, and `kind()` is implemented accordingly. -But we can also subclass `EscapeFunction` to, as in the above example, talk about all JS-escaping functions. +`Escaping`, as the name suggests, models various APIs that escape meta-characters. It has a member-predicate `getKind()` that tells you what sort of escaping the modeled function does. For example, if the result of that predicate is `"html"`, then this means that the escaping function is meant to make things safe to embed inside HTML. +`Escaping::Range` is subclassed to model various APIs, and `kind()` is implemented accordingly (this typically happens in library models). +But we can also subclass `Escaping`, as in the above example, where `HtmlEscaping` represents all HTML-escaping functions. You can, of course, do the same without the `::Range` pattern, but it's a little cumbersome: -If you only had an `abstract class EscapeFunction { ... }`, then `JsEscapeFunction` would need to be implemented in a slightly tricky way to prevent it from extending `EscapeFunction` (instead of refining it). You would have to give it a charpred `this instanceof EscapeFunction`, which looks useless but isn't. And additionally, you'd have to provide trivial `none()` overrides of all the abstract predicates defined in `EscapeFunction`. This is all pretty awkward, and we can avoid it by distinguishing between `EscapeFunction` and `EscapeFunction::Range`. +If you only had an `abstract class Escaping { ... }`, then `HtmlEscaping` would need to be implemented in a slightly tricky way to prevent it from extending `Escaping` (instead of refining it). You would have to give it a charpred `this instanceof Escaping`, which looks useless but isn't. And additionally, you'd have to provide trivial `none()` overrides of all the abstract predicates defined in `Escaping`. This is all pretty awkward, and we can avoid it by distinguishing between `Escaping` and `Escaping::Range`. ## Importing all subclasses of a class diff --git a/go/Makefile b/go/Makefile index b7da089c48a..419b1991775 100644 --- a/go/Makefile +++ b/go/Makefile @@ -14,7 +14,7 @@ CODEQL_PLATFORM = osx64 endif endif -CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh pre-finalize.cmd pre-finalize.sh index.cmd index.sh linux64 osx64 win64 tracing-config.lua) +CODEQL_TOOLS = $(addprefix codeql-tools/,autobuild.cmd autobuild.sh pre-finalize.cmd pre-finalize.sh index.cmd index.sh tracing-config.lua) EXTRACTOR_PACK_OUT = build/codeql-extractor-go diff --git a/go/codeql-tools/linux64/compiler-tracing.spec b/go/codeql-tools/linux64/compiler-tracing.spec deleted file mode 100644 index 2055555c21a..00000000000 --- a/go/codeql-tools/linux64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder: - order compiler - trace no -**/go: - invoke ${config_dir}/go-extractor - prepend --mimic - prepend "${compiler}" diff --git a/go/codeql-tools/osx64/compiler-tracing.spec b/go/codeql-tools/osx64/compiler-tracing.spec deleted file mode 100644 index 2055555c21a..00000000000 --- a/go/codeql-tools/osx64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder: - order compiler - trace no -**/go: - invoke ${config_dir}/go-extractor - prepend --mimic - prepend "${compiler}" diff --git a/go/codeql-tools/win64/compiler-tracing.spec b/go/codeql-tools/win64/compiler-tracing.spec deleted file mode 100644 index 76a6b011405..00000000000 --- a/go/codeql-tools/win64/compiler-tracing.spec +++ /dev/null @@ -1,7 +0,0 @@ -**/go-autobuilder.exe: - order compiler - trace no -**/go.exe: - invoke ${config_dir}/go-extractor.exe - prepend --mimic - prepend "${compiler}" diff --git a/go/ql/lib/change-notes/2022-09-12-uppercase.md b/go/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/go/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/go/ql/lib/semmle/go/Concepts.qll b/go/ql/lib/semmle/go/Concepts.qll index f3920c2ec5c..c6043ae46b3 100644 --- a/go/ql/lib/semmle/go/Concepts.qll +++ b/go/ql/lib/semmle/go/Concepts.qll @@ -16,16 +16,12 @@ import semmle.go.concepts.GeneratedFile * Extend this class to refine existing API models. If you want to model new APIs, * extend `SystemCommandExecution::Range` instead. */ -class SystemCommandExecution extends DataFlow::Node { - SystemCommandExecution::Range self; - - SystemCommandExecution() { this = self } - +class SystemCommandExecution extends DataFlow::Node instanceof SystemCommandExecution::Range { /** Gets the argument that specifies the command to be executed. */ - DataFlow::Node getCommandName() { result = self.getCommandName() } + DataFlow::Node getCommandName() { result = super.getCommandName() } /** Holds if this node is sanitized whenever it follows `--` in an argument list. */ - predicate doubleDashIsSanitizing() { self.doubleDashIsSanitizing() } + predicate doubleDashIsSanitizing() { super.doubleDashIsSanitizing() } } /** Provides a class for modeling new system-command execution APIs. */ @@ -52,22 +48,18 @@ module SystemCommandExecution { * Extend this class to refine existing API models. If you want to model new APIs, * extend `TemplateInstantiation::Range` instead. */ -class TemplateInstantiation extends DataFlow::Node { - TemplateInstantiation::Range self; - - TemplateInstantiation() { this = self } - +class TemplateInstantiation extends DataFlow::Node instanceof TemplateInstantiation::Range { /** * Gets the argument to this template instantiation that is the template being * instantiated. */ - DataFlow::Node getTemplateArgument() { result = self.getTemplateArgument() } + DataFlow::Node getTemplateArgument() { result = super.getTemplateArgument() } /** * Gets an argument to this template instantiation that is data being inserted * into the template. */ - DataFlow::Node getADataArgument() { result = self.getADataArgument() } + DataFlow::Node getADataArgument() { result = super.getADataArgument() } } /** Provides a class for modeling new template-instantiation APIs. */ @@ -100,13 +92,9 @@ module TemplateInstantiation { * Extend this class to refine existing API models. If you want to model new APIs, * extend `FileSystemAccess::Range` instead. */ -class FileSystemAccess extends DataFlow::Node { - FileSystemAccess::Range self; - - FileSystemAccess() { this = self } - +class FileSystemAccess extends DataFlow::Node instanceof FileSystemAccess::Range { /** Gets an argument to this file system access that is interpreted as a path. */ - DataFlow::Node getAPathArgument() { result = self.getAPathArgument() } + DataFlow::Node getAPathArgument() { result = super.getAPathArgument() } } /** Provides a class for modeling new file-system access APIs. */ @@ -125,17 +113,13 @@ module FileSystemAccess { } /** A function that escapes meta-characters to prevent injection attacks. */ -class EscapeFunction extends Function { - EscapeFunction::Range self; - - EscapeFunction() { this = self } - +class EscapeFunction extends Function instanceof EscapeFunction::Range { /** * The context that this function escapes for. * * Currently, this can be "js", "html", or "url". */ - string kind() { result = self.kind() } + string kind() { result = super.kind() } } /** Provides a class for modeling new escape-function APIs. */ @@ -161,7 +145,7 @@ module EscapeFunction { * JavaScript string literal. */ class JsEscapeFunction extends EscapeFunction { - JsEscapeFunction() { self.kind() = "js" } + JsEscapeFunction() { super.kind() = "js" } } /** @@ -170,7 +154,7 @@ class JsEscapeFunction extends EscapeFunction { * `

    {}

    `. */ class HtmlEscapeFunction extends EscapeFunction { - HtmlEscapeFunction() { self.kind() = "html" } + HtmlEscapeFunction() { super.kind() = "html" } } /** @@ -178,7 +162,7 @@ class HtmlEscapeFunction extends EscapeFunction { * of a URL. */ class UrlEscapeFunction extends EscapeFunction { - UrlEscapeFunction() { self.kind() = "url" } + UrlEscapeFunction() { super.kind() = "url" } } /** @@ -187,27 +171,23 @@ class UrlEscapeFunction extends EscapeFunction { * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexpPattern::Range` instead. */ -class RegexpPattern extends DataFlow::Node { - RegexpPattern::Range self; - - RegexpPattern() { this = self } - +class RegexpPattern extends DataFlow::Node instanceof RegexpPattern::Range { /** * Gets the node where this pattern is parsed as a part of a regular * expression. */ - DataFlow::Node getAParse() { result = self.getAParse() } + DataFlow::Node getAParse() { result = super.getAParse() } /** * Gets this regexp pattern as a string. */ - string getPattern() { result = self.getPattern() } + string getPattern() { result = super.getPattern() } /** * Gets a use of this pattern, either as itself in an argument to a function or as a compiled * regexp object. */ - DataFlow::Node getAUse() { result = self.getAUse() } + DataFlow::Node getAUse() { result = super.getAUse() } } /** Provides a class for modeling new regular-expression APIs. */ @@ -244,15 +224,11 @@ module RegexpPattern { * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexpMatchFunction::Range` instead. */ -class RegexpMatchFunction extends Function { - RegexpMatchFunction::Range self; - - RegexpMatchFunction() { this = self } - +class RegexpMatchFunction extends Function instanceof RegexpMatchFunction::Range { /** * Gets the function input that is the regexp being matched. */ - FunctionInput getRegexpArg() { result = self.getRegexpArg() } + FunctionInput getRegexpArg() { result = super.getRegexpArg() } /** * Gets the regexp pattern that is used in the call to this function `call`. @@ -264,12 +240,12 @@ class RegexpMatchFunction extends Function { /** * Gets the function input that is the string being matched against. */ - FunctionInput getValue() { result = self.getValue() } + FunctionInput getValue() { result = super.getValue() } /** * Gets the function output that is the Boolean result of the match function. */ - FunctionOutput getResult() { result = self.getResult() } + FunctionOutput getResult() { result = super.getResult() } } /** Provides a class for modeling new regular-expression matcher APIs. */ @@ -304,15 +280,11 @@ module RegexpMatchFunction { * Extend this class to refine existing API models. If you want to model new APIs, * extend `RegexpReplaceFunction::Range` instead. */ -class RegexpReplaceFunction extends Function { - RegexpReplaceFunction::Range self; - - RegexpReplaceFunction() { this = self } - +class RegexpReplaceFunction extends Function instanceof RegexpReplaceFunction::Range { /** * Gets the function input that is the regexp that matches text to replace. */ - FunctionInput getRegexpArg() { result = self.getRegexpArg() } + FunctionInput getRegexpArg() { result = super.getRegexpArg() } /** * Gets the regexp pattern that is used to match patterns to replace in the call to this function @@ -326,13 +298,13 @@ class RegexpReplaceFunction extends Function { * Gets the function input corresponding to the source value, that is, the value that is having * its contents replaced. */ - FunctionInput getSource() { result = self.getSource() } + FunctionInput getSource() { result = super.getSource() } /** * Gets the function output corresponding to the result, that is, the value after replacement has * occurred. */ - FunctionOutput getResult() { result = self.getResult() } + FunctionOutput getResult() { result = super.getResult() } } /** Provides a class for modeling new regular-expression replacer APIs. */ @@ -369,13 +341,9 @@ module RegexpReplaceFunction { * Extend this class to refine existing API models. If you want to model new APIs, * extend `LoggerCall::Range` instead. */ -class LoggerCall extends DataFlow::Node { - LoggerCall::Range self; - - LoggerCall() { this = self } - +class LoggerCall extends DataFlow::Node instanceof LoggerCall::Range { /** Gets a node that is a part of the logged message. */ - DataFlow::Node getAMessageComponent() { result = self.getAMessageComponent() } + DataFlow::Node getAMessageComponent() { result = super.getAMessageComponent() } } /** Provides a class for modeling new logging APIs. */ @@ -398,19 +366,15 @@ module LoggerCall { * Extend this class to refine existing API models. If you want to model new APIs, * extend `MarshalingFunction::Range` instead. */ -class MarshalingFunction extends Function { - MarshalingFunction::Range self; - - MarshalingFunction() { this = self } - +class MarshalingFunction extends Function instanceof MarshalingFunction::Range { /** Gets an input that is encoded by this function. */ - FunctionInput getAnInput() { result = self.getAnInput() } + FunctionInput getAnInput() { result = super.getAnInput() } /** Gets the output that contains the encoded data produced by this function. */ - FunctionOutput getOutput() { result = self.getOutput() } + FunctionOutput getOutput() { result = super.getOutput() } /** Gets an identifier for the format this function encodes into, such as "JSON". */ - string getFormat() { result = self.getFormat() } + string getFormat() { result = super.getFormat() } } /** Provides a class for modeling new marshaling APIs. */ @@ -439,19 +403,15 @@ module MarshalingFunction { * Extend this class to refine existing API models. If you want to model new APIs, * extend `UnmarshalingFunction::Range` instead. */ -class UnmarshalingFunction extends Function { - UnmarshalingFunction::Range self; - - UnmarshalingFunction() { this = self } - +class UnmarshalingFunction extends Function instanceof UnmarshalingFunction::Range { /** Gets an input that is decoded by this function. */ - FunctionInput getAnInput() { result = self.getAnInput() } + FunctionInput getAnInput() { result = super.getAnInput() } /** Gets the output that contains the decoded data produced by this function. */ - FunctionOutput getOutput() { result = self.getOutput() } + FunctionOutput getOutput() { result = super.getOutput() } /** Gets an identifier for the format this function decodes from, such as "JSON". */ - string getFormat() { result = self.getFormat() } + string getFormat() { result = super.getFormat() } } /** Provides a class for modeling new unmarshaling APIs. */ diff --git a/go/ql/lib/semmle/go/StringOps.qll b/go/ql/lib/semmle/go/StringOps.qll index 70f83328c05..d88aea71e23 100644 --- a/go/ql/lib/semmle/go/StringOps.qll +++ b/go/ql/lib/semmle/go/StringOps.qll @@ -12,20 +12,16 @@ module StringOps { * Extend this class to refine existing API models. If you want to model new APIs, * extend `StringOps::HasPrefix::Range` instead. */ - class HasPrefix extends DataFlow::Node { - HasPrefix::Range range; - - HasPrefix() { range = this } - + class HasPrefix extends DataFlow::Node instanceof HasPrefix::Range { /** * Gets the `A` in `strings.HasPrefix(A, B)`. */ - DataFlow::Node getBaseString() { result = range.getBaseString() } + DataFlow::Node getBaseString() { result = super.getBaseString() } /** * Gets the `B` in `strings.HasPrefix(A, B)`. */ - DataFlow::Node getSubstring() { result = range.getSubstring() } + DataFlow::Node getSubstring() { result = super.getSubstring() } /** * Gets the polarity of the check. @@ -33,7 +29,7 @@ module StringOps { * If the polarity is `false` the check returns `true` if the string does not start * with the given substring. */ - boolean getPolarity() { result = range.getPolarity() } + boolean getPolarity() { result = super.getPolarity() } } class StartsWith = HasPrefix; @@ -241,25 +237,21 @@ module StringOps { * Extend this class to refine existing API models. If you want to model new APIs, * extend `StringOps::Concatenation::Range` instead. */ - class Concatenation extends DataFlow::Node { - Concatenation::Range self; - - Concatenation() { this = self } - + class Concatenation extends DataFlow::Node instanceof Concatenation::Range { /** * Gets the `n`th operand of this string concatenation, if there is a data-flow node for it. */ - DataFlow::Node getOperand(int n) { result = self.getOperand(n) } + DataFlow::Node getOperand(int n) { result = super.getOperand(n) } /** * Gets the string value of the `n`th operand of this string concatenation, if it is a constant. */ - string getOperandStringValue(int n) { result = self.getOperandStringValue(n) } + string getOperandStringValue(int n) { result = super.getOperandStringValue(n) } /** * Gets the number of operands of this string concatenation. */ - int getNumOperand() { result = self.getNumOperand() } + int getNumOperand() { result = super.getNumOperand() } } /** Provides predicates and classes for working with string concatenations. */ diff --git a/go/ql/lib/semmle/go/concepts/GeneratedFile.qll b/go/ql/lib/semmle/go/concepts/GeneratedFile.qll index dec6872120c..d1067b60ad0 100644 --- a/go/ql/lib/semmle/go/concepts/GeneratedFile.qll +++ b/go/ql/lib/semmle/go/concepts/GeneratedFile.qll @@ -43,8 +43,4 @@ module GeneratedFile { * Extend this class to refine existing API models. If you want to model new APIs, * extend `GeneratedFile::Range` instead. */ -class GeneratedFile extends File { - GeneratedFile::Range self; - - GeneratedFile() { this = self } -} +class GeneratedFile extends File instanceof GeneratedFile::Range { } diff --git a/go/ql/lib/semmle/go/concepts/HTTP.qll b/go/ql/lib/semmle/go/concepts/HTTP.qll index 89a716d4f1a..770f577b1ff 100644 --- a/go/ql/lib/semmle/go/concepts/HTTP.qll +++ b/go/ql/lib/semmle/go/concepts/HTTP.qll @@ -5,7 +5,7 @@ import go /** Provides classes for modeling HTTP-related APIs. */ -module HTTP { +module Http { /** Provides a class for modeling new HTTP response-writer APIs. */ module ResponseWriter { /** @@ -31,11 +31,7 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::ResponseWriter::Range` instead. */ - class ResponseWriter extends Variable { - ResponseWriter::Range self; - - ResponseWriter() { this = self } - + class ResponseWriter extends Variable instanceof ResponseWriter::Range { /** Gets the body that is written in this HTTP response. */ ResponseBody getBody() { result.getResponseWriter() = this } @@ -47,8 +43,8 @@ module HTTP { /** Gets a data-flow node that is a use of this response writer. */ DataFlow::Node getANode() { - result = self.getANode() or - result.(DataFlow::PostUpdateNode).getPreUpdateNode() = self.getANode() + result = super.getANode() or + result.(DataFlow::PostUpdateNode).getPreUpdateNode() = super.getANode() } } @@ -100,19 +96,15 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::HeaderWrite::Range` instead. */ - class HeaderWrite extends DataFlow::ExprNode { - HeaderWrite::Range self; - - HeaderWrite() { this = self } - + class HeaderWrite extends DataFlow::ExprNode instanceof HeaderWrite::Range { /** Gets the (lower-case) name of a header set by this definition. */ - string getHeaderName() { result = self.getHeaderName() } + string getHeaderName() { result = super.getHeaderName() } /** Gets the value of the header set by this definition. */ - string getHeaderValue() { result = self.getHeaderValue() } + string getHeaderValue() { result = super.getHeaderValue() } /** Holds if this header write defines the header `header`. */ - predicate definesHeader(string header, string value) { self.definesHeader(header, value) } + predicate definesHeader(string header, string value) { super.definesHeader(header, value) } /** * Gets the node representing the name of the header defined by this write. @@ -121,13 +113,13 @@ module HTTP { * sets the `Content-Type` header) may not have such a node, so callers should use * `getHeaderName` in preference to this method). */ - DataFlow::Node getName() { result = self.getName() } + DataFlow::Node getName() { result = super.getName() } /** Gets the node representing the value of the header defined by this write. */ - DataFlow::Node getValue() { result = self.getValue() } + DataFlow::Node getValue() { result = super.getValue() } /** Gets the response writer associated with this header write, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } + ResponseWriter getResponseWriter() { result = super.getResponseWriter() } } /** A data-flow node whose value is written to an HTTP header. */ @@ -171,11 +163,7 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::RequestBody::Range` instead. */ - class RequestBody extends DataFlow::Node { - RequestBody::Range self; - - RequestBody() { this = self } - } + class RequestBody extends DataFlow::Node instanceof RequestBody::Range { } /** Provides a class for modeling new HTTP response-body APIs. */ module ResponseBody { @@ -191,7 +179,7 @@ module HTTP { /** Gets a content-type associated with this body. */ string getAContentType() { - exists(HTTP::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | + exists(Http::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | hw.getHeaderName() = "content-type" and result = hw.getHeaderValue() ) @@ -201,7 +189,7 @@ module HTTP { /** Gets a dataflow node for a content-type associated with this body. */ DataFlow::Node getAContentTypeNode() { - exists(HTTP::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | + exists(Http::HeaderWrite hw | hw = this.getResponseWriter().getAHeaderWrite() | hw.getHeaderName() = "content-type" and result = hw.getValue() ) @@ -215,19 +203,15 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::ResponseBody::Range` instead. */ - class ResponseBody extends DataFlow::Node { - ResponseBody::Range self; - - ResponseBody() { this = self } - + class ResponseBody extends DataFlow::Node instanceof ResponseBody::Range { /** Gets the response writer associated with this header write, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } + ResponseWriter getResponseWriter() { result = super.getResponseWriter() } /** Gets a content-type associated with this body. */ - string getAContentType() { result = self.getAContentType() } + string getAContentType() { result = super.getAContentType() } /** Gets a dataflow node for a content-type associated with this body. */ - DataFlow::Node getAContentTypeNode() { result = self.getAContentTypeNode() } + DataFlow::Node getAContentTypeNode() { result = super.getAContentTypeNode() } } /** Provides a class for modeling new HTTP template response-body APIs. */ @@ -250,13 +234,9 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::TemplateResponseBody::Range` instead. */ - class TemplateResponseBody extends ResponseBody { - override TemplateResponseBody::Range self; - - TemplateResponseBody() { this = self } - + class TemplateResponseBody extends ResponseBody instanceof TemplateResponseBody::Range { /** Gets the read of the variable inside the template where this value is read. */ - HtmlTemplate::TemplateRead getRead() { result = self.getRead() } + HtmlTemplate::TemplateRead getRead() { result = super.getRead() } } /** Provides a class for modeling new HTTP client request APIs. */ @@ -285,15 +265,11 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::ClientRequest::Range` instead. */ - class ClientRequest extends DataFlow::Node { - ClientRequest::Range self; - - ClientRequest() { this = self } - + class ClientRequest extends DataFlow::Node instanceof ClientRequest::Range { /** * Gets the URL of the request. */ - DataFlow::Node getUrl() { result = self.getUrl() } + DataFlow::Node getUrl() { result = super.getUrl() } } /** Provides a class for modeling new HTTP redirect APIs. */ @@ -337,16 +313,12 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::Redirect::Range` instead. */ - class Redirect extends DataFlow::Node { - Redirect::Range self; - - Redirect() { this = self } - + class Redirect extends DataFlow::Node instanceof Redirect::Range { /** Gets the data-flow node representing the URL being redirected to. */ - DataFlow::Node getUrl() { result = self.getUrl() } + DataFlow::Node getUrl() { result = super.getUrl() } /** Gets the response writer that this redirect is sent on, if any. */ - ResponseWriter getResponseWriter() { result = self.getResponseWriter() } + ResponseWriter getResponseWriter() { result = super.getResponseWriter() } } /** Provides a class for modeling new HTTP handler APIs. */ @@ -369,12 +341,11 @@ module HTTP { * Extend this class to refine existing API models. If you want to model new APIs, * extend `HTTP::RequestHandler::Range` instead. */ - class RequestHandler extends DataFlow::Node { - RequestHandler::Range self; - - RequestHandler() { this = self } - + class RequestHandler extends DataFlow::Node instanceof RequestHandler::Range { /** Gets a node that is used in a check that is tested before this handler is run. */ - predicate guardedBy(DataFlow::Node check) { self.guardedBy(check) } + predicate guardedBy(DataFlow::Node check) { super.guardedBy(check) } } } + +/** DEPRECATED: Alias for Http */ +deprecated module HTTP = Http; diff --git a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll index 7f4d87adbb5..ea3f757b440 100644 --- a/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll +++ b/go/ql/lib/semmle/go/dataflow/internal/DataFlowNodes.qll @@ -341,13 +341,9 @@ module Public { } /** A function, viewed as a node in a data flow graph. */ - class FunctionNode extends Node { - FunctionNode::Range self; - - FunctionNode() { this = self } - + class FunctionNode extends Node instanceof FunctionNode::Range { /** Gets the `i`th parameter of this function. */ - ParameterNode getParameter(int i) { result = self.getParameter(i) } + ParameterNode getParameter(int i) { result = super.getParameter(i) } /** Gets a parameter of this function. */ ParameterNode getAParameter() { result = this.getParameter(_) } @@ -356,18 +352,18 @@ module Public { int getNumParameter() { result = count(this.getAParameter()) } /** Gets the name of this function, if it has one. */ - string getName() { result = self.getName() } + string getName() { result = super.getName() } /** * Gets the dataflow node holding the value of the receiver, if any. */ - ReceiverNode getReceiver() { result = self.getReceiver() } + ReceiverNode getReceiver() { result = super.getReceiver() } /** * Gets a value returned by the given function via a return statement or an assignment to a * result variable. */ - ResultNode getAResult() { result = self.getAResult() } + ResultNode getAResult() { result = super.getAResult() } /** * Gets the data-flow node corresponding to the `i`th result of this function. @@ -379,7 +375,7 @@ module Public { * * Note that this predicate has no result for function literals. */ - Function getFunction() { result = self.getFunction() } + Function getFunction() { result = super.getFunction() } } /** A representation of a function that is declared in the module scope. */ diff --git a/go/ql/lib/semmle/go/frameworks/Beego.qll b/go/ql/lib/semmle/go/frameworks/Beego.qll index edcf383c6be..6d927112584 100644 --- a/go/ql/lib/semmle/go/frameworks/Beego.qll +++ b/go/ql/lib/semmle/go/frameworks/Beego.qll @@ -114,7 +114,7 @@ module Beego { } } - private class BeegoOutputInstance extends HTTP::ResponseWriter::Range { + private class BeegoOutputInstance extends Http::ResponseWriter::Range { SsaWithFields v; BeegoOutputInstance() { @@ -131,7 +131,7 @@ module Beego { } } - private class BeegoHeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::MethodCallNode { + private class BeegoHeaderWrite extends Http::HeaderWrite::Range, DataFlow::MethodCallNode { string methodName; BeegoHeaderWrite() { @@ -142,7 +142,7 @@ module Beego { override DataFlow::Node getName() { methodName = "Header" and result = this.getArgument(0) } override string getHeaderName() { - result = HTTP::HeaderWrite::Range.super.getHeaderName() + result = Http::HeaderWrite::Range.super.getHeaderName() or methodName = "ContentType" and result = "content-type" } @@ -153,12 +153,12 @@ module Beego { else result = this.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { + override Http::ResponseWriter getResponseWriter() { result.(BeegoOutputInstance).getAHeaderObject() = this } } - private class BeegoResponseBody extends HTTP::ResponseBody::Range { + private class BeegoResponseBody extends Http::ResponseBody::Range { DataFlow::MethodCallNode call; string methodName; @@ -170,7 +170,7 @@ module Beego { methodName in ["Body", "JSON", "JSONP", "ServeFormatted", "XML", "YAML"] } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = call.getReceiver() } + override Http::ResponseWriter getResponseWriter() { result.getANode() = call.getReceiver() } override string getAContentType() { // Super-method provides content-types for `Body`, which requires us to search @@ -192,7 +192,7 @@ module Beego { } } - private class ControllerResponseBody extends HTTP::ResponseBody::Range { + private class ControllerResponseBody extends Http::ResponseBody::Range { string name; ControllerResponseBody() { @@ -203,7 +203,7 @@ module Beego { ) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } override string getAContentType() { // Actually SetData can serve JSON, XML or YAML depending on the incoming @@ -213,7 +213,7 @@ module Beego { } } - private class ContextResponseBody extends HTTP::ResponseBody::Range { + private class ContextResponseBody extends Http::ResponseBody::Range { string name; ContextResponseBody() { @@ -224,7 +224,7 @@ module Beego { ) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } // Neither method is likely to be used with well-typed data such as JSON output, // because there are better methods to do this. Assume the Content-Type could @@ -314,7 +314,7 @@ module Beego { } } - private class RedirectMethods extends HTTP::Redirect::Range, DataFlow::CallNode { + private class RedirectMethods extends Http::Redirect::Range, DataFlow::CallNode { string package; string className; @@ -333,7 +333,7 @@ module Beego { className = "Context" and result = this.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } private class UtilsTaintPropagators extends TaintTracking::FunctionModel { diff --git a/go/ql/lib/semmle/go/frameworks/Echo.qll b/go/ql/lib/semmle/go/frameworks/Echo.qll index df58b492b87..c4d243b504c 100644 --- a/go/ql/lib/semmle/go/frameworks/Echo.qll +++ b/go/ql/lib/semmle/go/frameworks/Echo.qll @@ -29,7 +29,7 @@ private module Echo { /** * Data from a `Context` interface method that is not generally exploitable for open-redirect attacks. */ - private class EchoContextRedirectUnexploitableSource extends HTTP::Redirect::UnexploitableSource { + private class EchoContextRedirectUnexploitableSource extends Http::Redirect::UnexploitableSource { EchoContextRedirectUnexploitableSource() { exists(DataFlow::MethodCallNode call, string methodName | methodName = ["FormValue", "FormParams", "FormFile", "MultipartForm", "Cookie", "Cookies"] and @@ -77,14 +77,14 @@ private module Echo { /** * `echo.Context` methods which set the content-type to `text/html` and write a result in one operation. */ - private class EchoHtmlOutputs extends HTTP::ResponseBody::Range { + private class EchoHtmlOutputs extends Http::ResponseBody::Range { EchoHtmlOutputs() { exists(Method m | m.hasQualifiedName(packagePath(), "Context", ["HTML", "HTMLBlob"]) | this = m.getACall().getArgument(1) ) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } override string getAContentType() { result = "text/html" } } @@ -92,7 +92,7 @@ private module Echo { /** * `echo.Context` methods which take a content-type as a parameter. */ - private class EchoParameterizedOutputs extends HTTP::ResponseBody::Range { + private class EchoParameterizedOutputs extends Http::ResponseBody::Range { DataFlow::CallNode callNode; EchoParameterizedOutputs() { @@ -101,7 +101,7 @@ private module Echo { ) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } override DataFlow::Node getAContentTypeNode() { result = callNode.getArgument(1) } } @@ -109,7 +109,7 @@ private module Echo { /** * The `echo.Context.Redirect` method. */ - private class EchoRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode { + private class EchoRedirectMethod extends Http::Redirect::Range, DataFlow::CallNode { EchoRedirectMethod() { exists(Method m | m.hasQualifiedName(packagePath(), "Context", "Redirect") | this = m.getACall() @@ -118,6 +118,6 @@ private module Echo { override DataFlow::Node getUrl() { result = this.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } } diff --git a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll index 87c091fc3c5..42fb474a5f0 100644 --- a/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll +++ b/go/ql/lib/semmle/go/frameworks/ElazarlGoproxy.qll @@ -11,7 +11,7 @@ module ElazarlGoproxy { /** Gets the package name. */ string packagePath() { result = package("github.com/elazarl/goproxy", "") } - private class NewResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class NewResponse extends Http::HeaderWrite::Range, DataFlow::CallNode { NewResponse() { this.getTarget().hasQualifiedName(packagePath(), "NewResponse") } override string getHeaderName() { this.definesHeader(result, _) } @@ -28,21 +28,21 @@ module ElazarlGoproxy { header = "content-type" and value = this.getArgument(1).getStringValue() } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } /** A body argument to a `NewResponse` call. */ - private class NewResponseBody extends HTTP::ResponseBody::Range { + private class NewResponseBody extends Http::ResponseBody::Range { NewResponse call; NewResponseBody() { this = call.getArgument(3) } override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } - private class TextResponse extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class TextResponse extends Http::HeaderWrite::Range, DataFlow::CallNode { TextResponse() { this.getTarget().hasQualifiedName(packagePath(), "TextResponse") } override string getHeaderName() { this.definesHeader(result, _) } @@ -59,22 +59,22 @@ module ElazarlGoproxy { header = "content-type" and value = "text/plain" } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } /** A body argument to a `TextResponse` call. */ - private class TextResponseBody extends HTTP::ResponseBody::Range, TextResponse { + private class TextResponseBody extends Http::ResponseBody::Range, TextResponse { TextResponse call; TextResponseBody() { this = call.getArgument(2) } override DataFlow::Node getAContentTypeNode() { result = call.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } /** A handler attached to a goproxy proxy type. */ - private class ProxyHandler extends HTTP::RequestHandler::Range { + private class ProxyHandler extends Http::RequestHandler::Range { DataFlow::MethodCallNode handlerReg; ProxyHandler() { diff --git a/go/ql/lib/semmle/go/frameworks/Email.qll b/go/ql/lib/semmle/go/frameworks/Email.qll index 049af5f87bf..340cdad3d10 100644 --- a/go/ql/lib/semmle/go/frameworks/Email.qll +++ b/go/ql/lib/semmle/go/frameworks/Email.qll @@ -9,11 +9,7 @@ import go * Extend this class to refine existing API models. If you want to model new APIs, * extend `EmailData::Range` instead. */ -class EmailData extends DataFlow::Node { - EmailData::Range self; - - EmailData() { this = self } -} +class EmailData extends DataFlow::Node instanceof EmailData::Range { } /** Provides classes for working with data that is incorporated into an email. */ module EmailData { diff --git a/go/ql/lib/semmle/go/frameworks/Macaron.qll b/go/ql/lib/semmle/go/frameworks/Macaron.qll index a38b2b20da0..f107ec208ee 100644 --- a/go/ql/lib/semmle/go/frameworks/Macaron.qll +++ b/go/ql/lib/semmle/go/frameworks/Macaron.qll @@ -5,7 +5,7 @@ import go private module Macaron { - private class Context extends HTTP::ResponseWriter::Range { + private class Context extends Http::ResponseWriter::Range { SsaWithFields v; Context() { @@ -18,13 +18,13 @@ private module Macaron { override DataFlow::Node getANode() { result = v.similar().getAUse().getASuccessor*() } } - private class RedirectCall extends HTTP::Redirect::Range, DataFlow::MethodCallNode { + private class RedirectCall extends Http::Redirect::Range, DataFlow::MethodCallNode { RedirectCall() { this.getTarget().hasQualifiedName("gopkg.in/macaron.v1", "Context", "Redirect") } override DataFlow::Node getUrl() { result = this.getArgument(0) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } } } diff --git a/go/ql/lib/semmle/go/frameworks/NoSQL.qll b/go/ql/lib/semmle/go/frameworks/NoSQL.qll index 578cf67d33f..ff8190d854d 100644 --- a/go/ql/lib/semmle/go/frameworks/NoSQL.qll +++ b/go/ql/lib/semmle/go/frameworks/NoSQL.qll @@ -12,11 +12,7 @@ module NoSql { * Extend this class to refine existing API models. If you want to model new APIs, * extend `NoSQL::Query::Range` instead. */ - class Query extends DataFlow::Node { - Query::Range self; - - Query() { this = self } - } + class Query extends DataFlow::Node instanceof Query::Range { } /** Provides classes for working with NoSql queries. */ module Query { diff --git a/go/ql/lib/semmle/go/frameworks/Revel.qll b/go/ql/lib/semmle/go/frameworks/Revel.qll index 1ccad431b84..d006f46b2ee 100644 --- a/go/ql/lib/semmle/go/frameworks/Revel.qll +++ b/go/ql/lib/semmle/go/frameworks/Revel.qll @@ -114,7 +114,7 @@ module Revel { * We look particularly for html file extensions, since these are the only ones we currently have special rules * for (in particular, detecting XSS vulnerabilities). */ - private class ControllerRenderMethods extends HTTP::ResponseBody::Range { + private class ControllerRenderMethods extends Http::ResponseBody::Range { string contentType; ControllerRenderMethods() { @@ -149,7 +149,7 @@ module Revel { ) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } override string getAContentType() { result = contentType } } @@ -174,7 +174,7 @@ module Revel { * It is currently assumed that a tainted `value` in `Redirect(url, value)`, which calls `Sprintf(url, value)` * internally, cannot lead to an open redirect vulnerability. */ - private class ControllerRedirectMethod extends HTTP::Redirect::Range, DataFlow::CallNode { + private class ControllerRedirectMethod extends Http::Redirect::Range, DataFlow::CallNode { ControllerRedirectMethod() { exists(Method m | m.hasQualifiedName(packagePath(), "Controller", "Redirect") | this = m.getACall() @@ -183,7 +183,7 @@ module Revel { override DataFlow::Node getUrl() { result = this.getArgument(0) } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } } /** @@ -226,7 +226,7 @@ module Revel { /** * A write to a template argument field that is read raw inside of a template. */ - private class RawTemplateArgument extends HTTP::TemplateResponseBody::Range { + private class RawTemplateArgument extends Http::TemplateResponseBody::Range { RawTemplateRead read; RawTemplateArgument() { @@ -261,7 +261,7 @@ module Revel { override string getAContentType() { result = "text/html" } - override HTTP::ResponseWriter getResponseWriter() { none() } + override Http::ResponseWriter getResponseWriter() { none() } override HtmlTemplate::TemplateRead getRead() { result = read } } diff --git a/go/ql/lib/semmle/go/frameworks/SQL.qll b/go/ql/lib/semmle/go/frameworks/SQL.qll index 66566f7540c..322d17a420a 100644 --- a/go/ql/lib/semmle/go/frameworks/SQL.qll +++ b/go/ql/lib/semmle/go/frameworks/SQL.qll @@ -12,13 +12,9 @@ module SQL { * Extend this class to refine existing API models. If you want to model new APIs, * extend `SQL::Query::Range` instead. */ - class Query extends DataFlow::Node { - Query::Range self; - - Query() { this = self } - + class Query extends DataFlow::Node instanceof Query::Range { /** Gets a result of this query execution. */ - DataFlow::Node getAResult() { result = self.getAResult() } + DataFlow::Node getAResult() { result = super.getAResult() } /** * Gets a query string that is used as (part of) this SQL query. @@ -26,7 +22,7 @@ module SQL { * Note that this may not resolve all `QueryString`s that should be associated with this * query due to data flow. */ - QueryString getAQueryString() { result = self.getAQueryString() } + QueryString getAQueryString() { result = super.getAQueryString() } } /** @@ -59,11 +55,7 @@ module SQL { * Extend this class to refine existing API models. If you want to model new APIs, * extend `SQL::QueryString::Range` instead. */ - class QueryString extends DataFlow::Node { - QueryString::Range self; - - QueryString() { this = self } - } + class QueryString extends DataFlow::Node instanceof QueryString::Range { } /** Provides classes for working with SQL query strings. */ module QueryString { diff --git a/go/ql/lib/semmle/go/frameworks/Stdlib.qll b/go/ql/lib/semmle/go/frameworks/Stdlib.qll index b8481175216..db46167d205 100644 --- a/go/ql/lib/semmle/go/frameworks/Stdlib.qll +++ b/go/ql/lib/semmle/go/frameworks/Stdlib.qll @@ -128,7 +128,7 @@ module IntegerParser { } /** Provides models of commonly used functions in the `net/url` package. */ -module URL { +module Url { /** The `PathEscape` or `QueryEscape` function. */ class Escaper extends TaintTracking::FunctionModel { Escaper() { @@ -263,3 +263,6 @@ module URL { } } } + +/** DEPRECATED: Alias for Url */ +deprecated module URL = Url; diff --git a/go/ql/lib/semmle/go/frameworks/Testing.qll b/go/ql/lib/semmle/go/frameworks/Testing.qll index 2df3d6c0ddd..f56e58288e5 100644 --- a/go/ql/lib/semmle/go/frameworks/Testing.qll +++ b/go/ql/lib/semmle/go/frameworks/Testing.qll @@ -8,11 +8,7 @@ import go * Extend this class to refine existing models of testing frameworks. If you want to model new * frameworks, extend `TestCase::Range` instead. */ -class TestCase extends AstNode { - TestCase::Range self; - - TestCase() { this = self } -} +class TestCase extends AstNode instanceof TestCase::Range { } /** Provides classes for working with test cases. */ module TestCase { @@ -47,11 +43,7 @@ module TestCase { * Extend this class to refine existing models of testing frameworks. If you want to model new * frameworks, extend `TestFile::Range` instead. */ -class TestFile extends File { - TestFile::Range self; - - TestFile() { this = self } -} +class TestFile extends File instanceof TestFile::Range { } /** Provides classes for working with test files. */ module TestFile { diff --git a/go/ql/lib/semmle/go/frameworks/WebSocket.qll b/go/ql/lib/semmle/go/frameworks/WebSocket.qll index d3264467b45..44445b538b2 100644 --- a/go/ql/lib/semmle/go/frameworks/WebSocket.qll +++ b/go/ql/lib/semmle/go/frameworks/WebSocket.qll @@ -8,13 +8,9 @@ import go * Extend this class to refine existing API models. If you want to model new APIs, * extend `WebSocketRequestCall::Range` instead. */ -class WebSocketRequestCall extends DataFlow::CallNode { - WebSocketRequestCall::Range self; - - WebSocketRequestCall() { this = self } - +class WebSocketRequestCall extends DataFlow::CallNode instanceof WebSocketRequestCall::Range { /** Gets the URL of the request. */ - DataFlow::Node getRequestUrl() { result = self.getRequestUrl() } + DataFlow::Node getRequestUrl() { result = super.getRequestUrl() } } /** Provides classes for working with WebSocket request functions. */ @@ -143,13 +139,9 @@ class WebSocketReaderAsSource extends UntrustedFlowSource::Range { * Extend this class to refine existing API models. If you want to model new APIs, * extend `WebSocketReader::Range` instead. */ -class WebSocketReader extends Function { - WebSocketReader::Range self; - - WebSocketReader() { this = self } - +class WebSocketReader extends Function instanceof WebSocketReader::Range { /** Gets an output of this function containing data that is read from a WebSocket connection. */ - FunctionOutput getAnOutput() { result = self.getAnOutput() } + FunctionOutput getAnOutput() { result = super.getAnOutput() } } /** Provides classes for working with messages read from a WebSocket. */ diff --git a/go/ql/lib/semmle/go/frameworks/XPath.qll b/go/ql/lib/semmle/go/frameworks/XPath.qll index 896007f4641..e3b7282c4e3 100644 --- a/go/ql/lib/semmle/go/frameworks/XPath.qll +++ b/go/ql/lib/semmle/go/frameworks/XPath.qll @@ -12,11 +12,7 @@ module XPath { * Extend this class to refine existing API models. If you want to model new APIs, * extend `XPath::XPathExpressionString::Range` instead. */ - class XPathExpressionString extends DataFlow::Node { - XPathExpressionString::Range self; - - XPathExpressionString() { this = self } - } + class XPathExpressionString extends DataFlow::Node instanceof XPathExpressionString::Range { } /** Provides classes for working with XPath expression strings. */ module XPathExpressionString { diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll index c5e48e7d295..e0518ecf465 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/NetHttp.qll @@ -35,7 +35,7 @@ module NetHttp { } /** The declaration of a variable which either is or has a field that implements the http.ResponseWriter type */ - private class StdlibResponseWriter extends HTTP::ResponseWriter::Range { + private class StdlibResponseWriter extends Http::ResponseWriter::Range { SsaWithFields v; StdlibResponseWriter() { @@ -52,7 +52,7 @@ module NetHttp { } } - private class HeaderWriteCall extends HTTP::HeaderWrite::Range, DataFlow::MethodCallNode { + private class HeaderWriteCall extends Http::HeaderWrite::Range, DataFlow::MethodCallNode { HeaderWriteCall() { this.getTarget().hasQualifiedName("net/http", "Header", "Add") or this.getTarget().hasQualifiedName("net/http", "Header", "Set") @@ -62,7 +62,7 @@ module NetHttp { override DataFlow::Node getValue() { result = this.getArgument(1) } - override HTTP::ResponseWriter getResponseWriter() { + override Http::ResponseWriter getResponseWriter() { // find `v` in // ``` // header := v.Header() @@ -72,7 +72,7 @@ module NetHttp { } } - private class MapWrite extends HTTP::HeaderWrite::Range, DataFlow::Node { + private class MapWrite extends Http::HeaderWrite::Range, DataFlow::Node { Write write; DataFlow::Node index; DataFlow::Node rhs; @@ -86,7 +86,7 @@ module NetHttp { override DataFlow::Node getValue() { result = rhs } - override HTTP::ResponseWriter getResponseWriter() { + override Http::ResponseWriter getResponseWriter() { // find `v` in // ``` // header := v.Header() @@ -96,7 +96,7 @@ module NetHttp { } } - private class ResponseWriteHeaderCall extends HTTP::HeaderWrite::Range, DataFlow::MethodCallNode { + private class ResponseWriteHeaderCall extends Http::HeaderWrite::Range, DataFlow::MethodCallNode { ResponseWriteHeaderCall() { this.getTarget().implements("net/http", "ResponseWriter", "WriteHeader") } @@ -107,10 +107,10 @@ module NetHttp { override DataFlow::Node getValue() { result = this.getArgument(0) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } } - private class ResponseErrorCall extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class ResponseErrorCall extends Http::HeaderWrite::Range, DataFlow::CallNode { ResponseErrorCall() { this.getTarget().hasQualifiedName("net/http", "Error") } override string getHeaderName() { result = "status" } @@ -119,10 +119,10 @@ module NetHttp { override DataFlow::Node getValue() { result = this.getArgument(2) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) } } - private class RequestBody extends HTTP::RequestBody::Range, DataFlow::ExprNode { + private class RequestBody extends Http::RequestBody::Range, DataFlow::ExprNode { RequestBody() { exists(Function newRequest | newRequest.hasQualifiedName("net/http", "NewRequest") and @@ -137,7 +137,7 @@ module NetHttp { } } - private class ResponseBody extends HTTP::ResponseBody::Range, DataFlow::ArgumentNode { + private class ResponseBody extends Http::ResponseBody::Range, DataFlow::ArgumentNode { DataFlow::Node responseWriter; ResponseBody() { @@ -156,19 +156,19 @@ module NetHttp { ) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = responseWriter } + override Http::ResponseWriter getResponseWriter() { result.getANode() = responseWriter } } - private class RedirectCall extends HTTP::Redirect::Range, DataFlow::CallNode { + private class RedirectCall extends Http::Redirect::Range, DataFlow::CallNode { RedirectCall() { this.getTarget().hasQualifiedName("net/http", "Redirect") } override DataFlow::Node getUrl() { result = this.getArgument(2) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getArgument(0) } } /** A call to a function in the `net/http` package that performs an HTTP request to a URL. */ - private class RequestCall extends HTTP::ClientRequest::Range, DataFlow::CallNode { + private class RequestCall extends Http::ClientRequest::Range, DataFlow::CallNode { RequestCall() { exists(string functionName | ( @@ -185,7 +185,7 @@ module NetHttp { } /** A call to the Client.Do function in the `net/http` package. */ - private class ClientDo extends HTTP::ClientRequest::Range, DataFlow::MethodCallNode { + private class ClientDo extends Http::ClientRequest::Range, DataFlow::MethodCallNode { ClientDo() { this.getTarget().hasQualifiedName("net/http", "Client", "Do") } override DataFlow::Node getUrl() { @@ -212,7 +212,7 @@ module NetHttp { } /** A call to the `Transport.RoundTrip` function in the `net/http` package. */ - private class TransportRoundTrip extends HTTP::ClientRequest::Range, DataFlow::MethodCallNode { + private class TransportRoundTrip extends Http::ClientRequest::Range, DataFlow::MethodCallNode { TransportRoundTrip() { this.getTarget().hasQualifiedName("net/http", "Transport", "RoundTrip") } override DataFlow::Node getUrl() { @@ -239,7 +239,7 @@ module NetHttp { } /** Fields and methods of `net/http.Request` that are not generally exploitable in an open-redirect attack. */ - private class RedirectUnexploitableRequestFields extends HTTP::Redirect::UnexploitableSource { + private class RedirectUnexploitableRequestFields extends Http::Redirect::UnexploitableSource { RedirectUnexploitableRequestFields() { exists(Field f, string fieldName | f.hasQualifiedName("net/http", "Request", fieldName) and @@ -257,7 +257,7 @@ module NetHttp { } } - private class Handler extends HTTP::RequestHandler::Range { + private class Handler extends Http::RequestHandler::Range { DataFlow::CallNode handlerReg; Handler() { diff --git a/go/ql/lib/semmle/go/security/FlowSources.qll b/go/ql/lib/semmle/go/security/FlowSources.qll index c1e08710e06..de8fb5c3732 100644 --- a/go/ql/lib/semmle/go/security/FlowSources.qll +++ b/go/ql/lib/semmle/go/security/FlowSources.qll @@ -11,11 +11,7 @@ private import semmle.go.dataflow.ExternalFlow as ExternalFlow * Extend this class to refine existing API models. If you want to model new APIs, * extend `UntrustedFlowSource::Range` instead. */ -class UntrustedFlowSource extends DataFlow::Node { - UntrustedFlowSource::Range self; - - UntrustedFlowSource() { this = self } -} +class UntrustedFlowSource extends DataFlow::Node instanceof UntrustedFlowSource::Range { } /** Provides a class for modeling new sources of untrusted data. */ module UntrustedFlowSource { diff --git a/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll b/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll index d883c3c0101..80b9bb4a126 100644 --- a/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll +++ b/go/ql/lib/semmle/go/security/OpenUrlRedirectCustomizations.qll @@ -56,7 +56,7 @@ module OpenUrlRedirect { UntrustedFlowAsSource() { // exclude some fields and methods of URLs that are generally not attacker-controllable for // open redirect exploits - not this instanceof HTTP::Redirect::UnexploitableSource + not this instanceof Http::Redirect::UnexploitableSource } } @@ -64,7 +64,7 @@ module OpenUrlRedirect { * An HTTP redirect, considered as a sink for `Configuration`. */ class RedirectSink extends Sink, DataFlow::Node { - RedirectSink() { this = any(HTTP::Redirect redir).getUrl() } + RedirectSink() { this = any(Http::Redirect redir).getUrl() } } /** @@ -73,7 +73,7 @@ module OpenUrlRedirect { */ class LocationHeaderSink extends Sink, DataFlow::Node { LocationHeaderSink() { - exists(HTTP::HeaderWrite hw | hw.getHeaderName() = "location" | this = hw.getValue()) + exists(Http::HeaderWrite hw | hw.getHeaderName() = "location" | this = hw.getValue()) } } diff --git a/go/ql/lib/semmle/go/security/RequestForgeryCustomizations.qll b/go/ql/lib/semmle/go/security/RequestForgeryCustomizations.qll index b75b3503d55..b11157daa06 100644 --- a/go/ql/lib/semmle/go/security/RequestForgeryCustomizations.qll +++ b/go/ql/lib/semmle/go/security/RequestForgeryCustomizations.qll @@ -48,7 +48,7 @@ module RequestForgery { * The URL of an HTTP request, viewed as a sink for request forgery. */ private class ClientRequestUrlAsSink extends Sink { - HTTP::ClientRequest request; + Http::ClientRequest request; ClientRequestUrlAsSink() { this = request.getUrl() } diff --git a/go/ql/lib/semmle/go/security/SafeUrlFlowCustomizations.qll b/go/ql/lib/semmle/go/security/SafeUrlFlowCustomizations.qll index de0f95f8f3a..dbdb96a10a7 100644 --- a/go/ql/lib/semmle/go/security/SafeUrlFlowCustomizations.qll +++ b/go/ql/lib/semmle/go/security/SafeUrlFlowCustomizations.qll @@ -27,7 +27,7 @@ module SafeUrlFlow { /** * A method on a `net/url.URL` that is considered unsafe to use. */ - private class UnsafeUrlMethod extends URL::UrlGetter { + private class UnsafeUrlMethod extends Url::UrlGetter { UnsafeUrlMethod() { this.getName() = "Query" } } diff --git a/go/ql/lib/semmle/go/security/Xss.qll b/go/ql/lib/semmle/go/security/Xss.qll index 2ed5c5761a7..e9301646f77 100644 --- a/go/ql/lib/semmle/go/security/Xss.qll +++ b/go/ql/lib/semmle/go/security/Xss.qll @@ -46,14 +46,14 @@ module SharedXss { * a content type that does not (case-insensitively) contain the string "html". This * is to prevent us from flagging plain-text or JSON responses as vulnerable. */ - class HttpResponseBodySink extends Sink, HTTP::ResponseBody { + class HttpResponseBodySink extends Sink, Http::ResponseBody { HttpResponseBodySink() { not nonHtmlContentType(this) } } /** * An expression that is rendered as part of a template. */ - class RawTemplateInstantiationSink extends HttpResponseBodySink, HTTP::TemplateResponseBody { + class RawTemplateInstantiationSink extends HttpResponseBodySink, Http::TemplateResponseBody { override string getSinkKind() { result = "rawtemplate" } override Locatable getAssociatedLoc() { result = this.getRead().getEnclosingTextNode() } @@ -62,7 +62,7 @@ module SharedXss { /** * Holds if `body` may send a response with a content type other than HTML. */ - private predicate nonHtmlContentType(HTTP::ResponseBody body) { + private predicate nonHtmlContentType(Http::ResponseBody body) { not htmlTypeSpecified(body) and ( exists(body.getAContentType()) @@ -90,7 +90,7 @@ module SharedXss { /** * Holds if `body` specifies the response's content type to be HTML. */ - private predicate htmlTypeSpecified(HTTP::ResponseBody body) { + private predicate htmlTypeSpecified(Http::ResponseBody body) { body.getAContentType().regexpMatch("(?i).*html.*") } diff --git a/go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql b/go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql index a6ce8c8c8b6..4272d1cc27e 100644 --- a/go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql +++ b/go/ql/src/Security/CWE-020/IncompleteHostnameRegexp.ql @@ -33,7 +33,7 @@ predicate isIncompleteHostNameRegexpPattern(string pattern, string hostPart) { /** Holds if `b` sets the HTTP status code (represented by a pseudo-header named `status`) */ predicate writesHttpError(ReachableBasicBlock b) { - forex(HTTP::HeaderWrite hw | + forex(Http::HeaderWrite hw | hw.getHeaderName() = "status" and hw.asInstruction().getBasicBlock() = b | exists(string code | code.matches("4__") or code.matches("5__") | @@ -65,7 +65,7 @@ DataFlow::Node getASafeHandler() { } /** Holds if `regexp` is used in a check before `handler` is called. */ -predicate regexpGuardsHandler(RegexpPattern regexp, HTTP::RequestHandler handler) { +predicate regexpGuardsHandler(RegexpPattern regexp, Http::RequestHandler handler) { handler.guardedBy(DataFlow::exprNode(regexp.getAUse().asExpr().getParent*())) } @@ -99,7 +99,7 @@ class Config extends DataFlow::Configuration { override predicate isSink(DataFlow::Node sink) { sink instanceof RegexpPattern and - forall(HTTP::RequestHandler handler | regexpGuardsHandler(sink, handler) | + forall(Http::RequestHandler handler | regexpGuardsHandler(sink, handler) | not handler = getASafeHandler() ) and not regexpGuardsError(sink) diff --git a/go/ql/src/Security/CWE-209/StackTraceExposure.ql b/go/ql/src/Security/CWE-209/StackTraceExposure.ql index a2005663784..369c8109d47 100644 --- a/go/ql/src/Security/CWE-209/StackTraceExposure.ql +++ b/go/ql/src/Security/CWE-209/StackTraceExposure.ql @@ -60,7 +60,7 @@ class StackTraceExposureConfig extends TaintTracking::Configuration { node = any(DebugStackFunction f).getACall().getResult() } - override predicate isSink(DataFlow::Node node) { node instanceof HTTP::ResponseBody } + override predicate isSink(DataFlow::Node node) { node instanceof Http::ResponseBody } override predicate isSanitizer(DataFlow::Node node) { // Sanitise everything controlled by an is-debug-mode check. diff --git a/go/ql/src/experimental/CWE-918/SSRF.qll b/go/ql/src/experimental/CWE-918/SSRF.qll index b7a408902d0..cf264c8c0b6 100644 --- a/go/ql/src/experimental/CWE-918/SSRF.qll +++ b/go/ql/src/experimental/CWE-918/SSRF.qll @@ -74,7 +74,7 @@ module ServerSideRequestForgery { * The URL of an HTTP request, viewed as a sink for request forgery. */ private class ClientRequestUrlAsSink extends Sink { - HTTP::ClientRequest request; + Http::ClientRequest request; ClientRequestUrlAsSink() { this = request.getUrl() } diff --git a/go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql b/go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql index 4e4b452fb8e..372b1c21642 100644 --- a/go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql +++ b/go/ql/src/experimental/CWE-942/CorsMisconfiguration.ql @@ -41,14 +41,14 @@ string headerAllowCredentials() { result = "Access-Control-Allow-Credentials".to /** * An `Access-Control-Allow-Origin` header write. */ -class AllowOriginHeaderWrite extends HTTP::HeaderWrite { +class AllowOriginHeaderWrite extends Http::HeaderWrite { AllowOriginHeaderWrite() { this.getHeaderName() = headerAllowOrigin() } } /** * An `Access-Control-Allow-Credentials` header write. */ -class AllowCredentialsHeaderWrite extends HTTP::HeaderWrite { +class AllowCredentialsHeaderWrite extends Http::HeaderWrite { AllowCredentialsHeaderWrite() { this.getHeaderName() = headerAllowCredentials() } } diff --git a/go/ql/src/experimental/frameworks/CleverGo.qll b/go/ql/src/experimental/frameworks/CleverGo.qll index c061c416b88..99d57a46189 100644 --- a/go/ql/src/experimental/frameworks/CleverGo.qll +++ b/go/ql/src/experimental/frameworks/CleverGo.qll @@ -174,7 +174,7 @@ private module CleverGo { /** * Models HTTP redirects. */ - private class HttpRedirect extends HTTP::Redirect::Range, DataFlow::CallNode { + private class HttpRedirect extends Http::Redirect::Range, DataFlow::CallNode { string package; DataFlow::Node urlNode; @@ -191,13 +191,13 @@ private module CleverGo { override DataFlow::Node getUrl() { result = urlNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } } /** * Models HTTP ResponseBody where the content-type is static and non-modifiable. */ - private class HttpResponseBodyStaticContentType extends HTTP::ResponseBody::Range { + private class HttpResponseBodyStaticContentType extends Http::ResponseBody::Range { string contentTypeString; DataFlow::Node receiverNode; @@ -209,7 +209,7 @@ private module CleverGo { override string getAContentType() { result = contentTypeString } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the body; the content-type is implicitly set. @@ -304,7 +304,7 @@ private module CleverGo { /** * Models HTTP ResponseBody where the content-type can be dynamically set by the caller. */ - private class HttpResponseBodyDynamicContentType extends HTTP::ResponseBody::Range { + private class HttpResponseBodyDynamicContentType extends Http::ResponseBody::Range { DataFlow::Node contentTypeNode; DataFlow::Node receiverNode; @@ -316,7 +316,7 @@ private module CleverGo { override DataFlow::Node getAContentTypeNode() { result = contentTypeNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the body; the content-type is a parameter. @@ -352,7 +352,7 @@ private module CleverGo { /** * Models HTTP ResponseBody where only the body is set. */ - private class HttpResponseBodyNoContentType extends HTTP::ResponseBody::Range { + private class HttpResponseBodyNoContentType extends Http::ResponseBody::Range { DataFlow::Node receiverNode; HttpResponseBodyNoContentType() { @@ -361,7 +361,7 @@ private module CleverGo { ) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the body. The content-type is not defined. @@ -394,7 +394,7 @@ private module CleverGo { * Models HTTP header writers. * The write is done with a call where you can set both the key and the value of the header. */ - private class HeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class HeaderWrite extends Http::HeaderWrite::Range, DataFlow::CallNode { DataFlow::Node receiverNode; DataFlow::Node headerNameNode; DataFlow::Node headerValueNode; @@ -407,7 +407,7 @@ private module CleverGo { override DataFlow::Node getValue() { result = headerValueNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets a header with a key-value combination. @@ -437,7 +437,7 @@ private module CleverGo { /** * Models an HTTP static content-type header setter. */ - private class StaticContentTypeHeaderSetter extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class StaticContentTypeHeaderSetter extends Http::HeaderWrite::Range, DataFlow::CallNode { DataFlow::Node receiverNode; string valueString; @@ -453,7 +453,7 @@ private module CleverGo { override DataFlow::Node getValue() { none() } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the content-type header (implicit). @@ -494,7 +494,7 @@ private module CleverGo { /** * Models an HTTP dynamic content-type header setter. */ - private class DynamicContentTypeHeaderSetter extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class DynamicContentTypeHeaderSetter extends Http::HeaderWrite::Range, DataFlow::CallNode { DataFlow::Node receiverNode; DataFlow::Node valueNode; @@ -508,7 +508,7 @@ private module CleverGo { override DataFlow::Node getValue() { result = valueNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the content-type header via a parameter. diff --git a/go/ql/src/experimental/frameworks/Fiber.qll b/go/ql/src/experimental/frameworks/Fiber.qll index a3240a0b85a..3a7e71e204e 100644 --- a/go/ql/src/experimental/frameworks/Fiber.qll +++ b/go/ql/src/experimental/frameworks/Fiber.qll @@ -129,7 +129,7 @@ private module Fiber { /** * Models HTTP redirects. */ - private class Redirect extends HTTP::Redirect::Range, DataFlow::CallNode { + private class Redirect extends Http::Redirect::Range, DataFlow::CallNode { string package; DataFlow::Node urlNode; @@ -146,14 +146,14 @@ private module Fiber { override DataFlow::Node getUrl() { result = urlNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } + override Http::ResponseWriter getResponseWriter() { result.getANode() = this.getReceiver() } } /** * Models HTTP header writers. * The write is done with a call where you can set both the key and the value of the header. */ - private class HeaderWrite extends HTTP::HeaderWrite::Range, DataFlow::CallNode { + private class HeaderWrite extends Http::HeaderWrite::Range, DataFlow::CallNode { DataFlow::Node receiverNode; DataFlow::Node headerNameNode; DataFlow::Node headerValueNode; @@ -166,7 +166,7 @@ private module Fiber { override DataFlow::Node getValue() { result = headerValueNode } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets a header with a key-value combination. @@ -201,7 +201,7 @@ private module Fiber { /** * Models HTTP ResponseBody where the content-type is static and non-modifiable. */ - private class ResponseBodyStaticContentType extends HTTP::ResponseBody::Range { + private class ResponseBodyStaticContentType extends Http::ResponseBody::Range { string contentTypeString; DataFlow::Node receiverNode; @@ -213,7 +213,7 @@ private module Fiber { override string getAContentType() { result = contentTypeString } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the body; the content-type is implicitly set. @@ -248,7 +248,7 @@ private module Fiber { /** * Models HTTP ResponseBody where only the body is set. */ - private class ResponseBodyNoContentType extends HTTP::ResponseBody::Range { + private class ResponseBodyNoContentType extends Http::ResponseBody::Range { DataFlow::Node receiverNode; ResponseBodyNoContentType() { @@ -257,7 +257,7 @@ private module Fiber { ) } - override HTTP::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } + override Http::ResponseWriter getResponseWriter() { result.getANode() = receiverNode } } // Holds for a call that sets the body. The content-type is not defined. diff --git a/go/ql/test/experimental/frameworks/CleverGo/HeaderWrite.ql b/go/ql/test/experimental/frameworks/CleverGo/HeaderWrite.ql index 3f33ae98248..20dc73705a8 100644 --- a/go/ql/test/experimental/frameworks/CleverGo/HeaderWrite.ql +++ b/go/ql/test/experimental/frameworks/CleverGo/HeaderWrite.ql @@ -11,7 +11,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { // Dynamic key-value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( @@ -26,7 +26,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { ) or // Static key, dynamic value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( @@ -41,7 +41,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { ) or // Static key, static value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( diff --git a/go/ql/test/experimental/frameworks/CleverGo/HttpRedirect.ql b/go/ql/test/experimental/frameworks/CleverGo/HttpRedirect.ql index 5ceb813cec9..4b7bcb7823c 100644 --- a/go/ql/test/experimental/frameworks/CleverGo/HttpRedirect.ql +++ b/go/ql/test/experimental/frameworks/CleverGo/HttpRedirect.ql @@ -9,7 +9,7 @@ class HttpRedirectTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "redirectUrl" and - exists(HTTP::Redirect rd | + exists(Http::Redirect rd | rd.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and element = rd.getUrl().toString() and diff --git a/go/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.ql b/go/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.ql index 33c3f94ec07..a2b3dc62167 100644 --- a/go/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.ql +++ b/go/ql/test/experimental/frameworks/CleverGo/HttpResponseBody.ql @@ -8,7 +8,7 @@ class HttpResponseBodyTest extends InlineExpectationsTest { override string getARelevantTag() { result = ["contentType", "responseBody"] } override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(HTTP::ResponseBody rd | + exists(Http::ResponseBody rd | rd.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( diff --git a/go/ql/test/experimental/frameworks/Fiber/HeaderWrite.ql b/go/ql/test/experimental/frameworks/Fiber/HeaderWrite.ql index d83883f45a7..0130172e7d0 100644 --- a/go/ql/test/experimental/frameworks/Fiber/HeaderWrite.ql +++ b/go/ql/test/experimental/frameworks/Fiber/HeaderWrite.ql @@ -11,7 +11,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { // Dynamic key-value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( @@ -26,7 +26,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { ) or // Static key, dynamic value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( @@ -41,7 +41,7 @@ class HttpHeaderWriteTest extends InlineExpectationsTest { ) or // Static key, static value header: - exists(HTTP::HeaderWrite hw | + exists(Http::HeaderWrite hw | hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( diff --git a/go/ql/test/experimental/frameworks/Fiber/Redirect.ql b/go/ql/test/experimental/frameworks/Fiber/Redirect.ql index 68d5bee465c..37bb0f97bfb 100644 --- a/go/ql/test/experimental/frameworks/Fiber/Redirect.ql +++ b/go/ql/test/experimental/frameworks/Fiber/Redirect.ql @@ -9,7 +9,7 @@ class HttpRedirectTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "redirectUrl" and - exists(HTTP::Redirect rd | + exists(Http::Redirect rd | rd.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and element = rd.getUrl().toString() and diff --git a/go/ql/test/experimental/frameworks/Fiber/ResponseBody.ql b/go/ql/test/experimental/frameworks/Fiber/ResponseBody.ql index 4361b2fda6a..74a9008749d 100644 --- a/go/ql/test/experimental/frameworks/Fiber/ResponseBody.ql +++ b/go/ql/test/experimental/frameworks/Fiber/ResponseBody.ql @@ -8,7 +8,7 @@ class HttpResponseBodyTest extends InlineExpectationsTest { override string getARelevantTag() { result = ["contentType", "responseBody"] } override predicate hasActualResult(Location location, string element, string tag, string value) { - exists(HTTP::ResponseBody rd | + exists(Http::ResponseBody rd | rd.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and ( diff --git a/go/ql/test/library-tests/semmle/go/concepts/HTTP/Handler.ql b/go/ql/test/library-tests/semmle/go/concepts/HTTP/Handler.ql index e0b0acd426b..c6972eba275 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/HTTP/Handler.ql +++ b/go/ql/test/library-tests/semmle/go/concepts/HTTP/Handler.ql @@ -8,7 +8,7 @@ class HttpHandler extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "handler" and - exists(HTTP::RequestHandler h, DataFlow::Node check | + exists(Http::RequestHandler h, DataFlow::Node check | element = h.toString() and value = check.toString() | h.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), diff --git a/go/ql/test/library-tests/semmle/go/concepts/HTTP/Header.ql b/go/ql/test/library-tests/semmle/go/concepts/HTTP/Header.ql index e5449bf3e53..0ec82e5ad89 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/HTTP/Header.ql +++ b/go/ql/test/library-tests/semmle/go/concepts/HTTP/Header.ql @@ -1,6 +1,6 @@ import go -from HTTP::HeaderWrite headerWrite, string name, string value, string definedName, string definedVal +from Http::HeaderWrite headerWrite, string name, string value, string definedName, string definedVal where ( name = headerWrite.getName().toString() diff --git a/go/ql/test/library-tests/semmle/go/concepts/HTTP/RequestBody.ql b/go/ql/test/library-tests/semmle/go/concepts/HTTP/RequestBody.ql index d49b7e95bb4..90d58b2db8d 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/HTTP/RequestBody.ql +++ b/go/ql/test/library-tests/semmle/go/concepts/HTTP/RequestBody.ql @@ -1,4 +1,4 @@ import go -from HTTP::RequestBody rb +from Http::RequestBody rb select rb diff --git a/go/ql/test/library-tests/semmle/go/concepts/HTTP/ResponseBody.ql b/go/ql/test/library-tests/semmle/go/concepts/HTTP/ResponseBody.ql index d8d89b7f5bb..80394628bfc 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/HTTP/ResponseBody.ql +++ b/go/ql/test/library-tests/semmle/go/concepts/HTTP/ResponseBody.ql @@ -1,4 +1,4 @@ import go -from HTTP::ResponseBody rb +from Http::ResponseBody rb select rb diff --git a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql index 106d28b48f5..0b07a0a20f0 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/ElazarlGoproxy/test.ql @@ -23,7 +23,7 @@ class HeaderWriteTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "headerwrite" and - exists(HTTP::HeaderWrite hw, string name, string val | element = hw.toString() | + exists(Http::HeaderWrite hw, string name, string val | element = hw.toString() | hw.definesHeader(name, val) and value = name + ":" + val and hw.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql b/go/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql index 813ad53f2e0..c73e0dac04c 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/Macaron/Redirect.ql @@ -1,4 +1,4 @@ import go -from HTTP::Redirect redir +from Http::Redirect redir select redir, redir.getResponseWriter() diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Revel/test.ql b/go/ql/test/library-tests/semmle/go/frameworks/Revel/test.ql index e3d2cd16be4..9d2b876d803 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Revel/test.ql +++ b/go/ql/test/library-tests/semmle/go/frameworks/Revel/test.ql @@ -39,7 +39,7 @@ class HttpResponseBodyTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { tag = "responsebody" and - exists(HTTP::ResponseBody rb | + exists(Http::ResponseBody rb | rb.hasLocationInfo(location.getFile().getAbsolutePath(), location.getStartLine(), location.getStartColumn(), location.getEndLine(), location.getEndColumn()) and element = rb.toString() and diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index d3599459e66..029f1b41e91 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -7,6 +7,7 @@ android.os,,2,122,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,41,81 android.util,6,16,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,16,, android.webkit,3,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3,,,,2,, android.widget,,1,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,1, +androidx.core.app,6,,95,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,12,83 androidx.slice,2,5,88,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,5,,27,61 cn.hutool.core.codec,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, com.esotericsoftware.kryo.io,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 1d209ea05d2..a9b34147fbb 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -18,6 +18,6 @@ Java framework & library support Java Standard Library,``java.*``,3,585,130,28,,,7,,,10 Java extensions,"``javax.*``, ``jakarta.*``",63,609,32,,,4,,1,1,2 `Spring `_,``org.springframework.*``,29,477,101,,,,19,14,,29 - Others,"``androidx.slice``, ``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``kotlin``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.logging.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jboss.logging``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",65,2229,944,10,,,14,18,,5 - Totals,,217,8331,1496,129,6,10,107,33,1,86 + Others,"``androidx.core.app``, ``androidx.slice``, ``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``groovy.lang``, ``groovy.util``, ``jodd.json``, ``kotlin``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.logging.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.hibernate``, ``org.jboss.logging``, ``org.jdbi.v3.core``, ``org.jooq``, ``org.mvel2``, ``org.scijava.log``, ``org.slf4j``, ``org.xml.sax``, ``org.xmlpull.v1``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",65,2324,950,10,,,14,18,,5 + Totals,,217,8426,1502,129,6,10,107,33,1,86 diff --git a/java/kotlin-extractor/build.py b/java/kotlin-extractor/build.py index 3f14eb0b28e..a4da1a2ea23 100755 --- a/java/kotlin-extractor/build.py +++ b/java/kotlin-extractor/build.py @@ -86,7 +86,8 @@ def compile_to_dir(srcs, classpath, java_classpath, output): run_process([kotlinc, # kotlinc can default to 256M, which isn't enough when we are extracting the build '-J-Xmx2G', - '-Xopt-in=kotlin.RequiresOptIn', + '-Werror', + '-opt-in=kotlin.RequiresOptIn', '-d', output, '-module-name', 'codeql-kotlin-extractor', '-no-reflect', '-no-stdlib', diff --git a/java/kotlin-extractor/src/main/java/com/semmle/extractor/java/OdasaOutput.java b/java/kotlin-extractor/src/main/java/com/semmle/extractor/java/OdasaOutput.java index 73dc090f69b..22fcbdf0011 100644 --- a/java/kotlin-extractor/src/main/java/com/semmle/extractor/java/OdasaOutput.java +++ b/java/kotlin-extractor/src/main/java/com/semmle/extractor/java/OdasaOutput.java @@ -255,17 +255,6 @@ public class OdasaOutput { * Trap writers. */ - /** - * A {@link TrapFileManager} to output facts for the given source file, - * or null if the source file should not be populated. - */ - private TrapFileManager getTrapWriterForCurrentSourceFile() { - File trapFile = getTrapFileForCurrentSourceFile(); - if (trapFile==null) - return null; - return trapWriter(trapFile, null, null); - } - /** * Get a {@link TrapFileManager} to write members * about a declaration, or null if the declaration shouldn't be populated. diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt index d6f25d73efa..3b58400eca5 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinExtractorExtension.kt @@ -354,7 +354,12 @@ private fun getTrapFileWriter(compression: Compression, logger: FileLogger, trap return when (compression) { Compression.NONE -> NonCompressedTrapFileWriter(logger, trapFileName) Compression.GZIP -> GZipCompressedTrapFileWriter(logger, trapFileName) - Compression.BROTLI -> throw Exception("Brotli compression is not supported by the Kotlin extractor") + Compression.BROTLI -> { + // Brotli should have been replaced with gzip earlier, but + // if we somehow manage to get here then keep going + logger.error("Impossible Brotli compression requested. Using Gzip instead.") + getTrapFileWriter(Compression.GZIP, logger, trapFileName) + } } } diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index e5199b6785e..45636455806 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -1333,6 +1333,14 @@ open class KotlinFileExtractor( val receiverClass = receiverType.classifier.owner as? IrClass ?: return listOf() val ancestorTypes = ArrayList() + // KFunctionX doesn't implement FunctionX on versions before 1.7.0: + if ((callTarget.name.asString() == "invoke") && + (receiverClass.fqNameWhenAvailable?.asString()?.startsWith("kotlin.reflect.KFunction") == true) && + (callTarget.parentClassOrNull?.fqNameWhenAvailable?.asString()?.startsWith("kotlin.Function") == true) + ) { + return receiverType.arguments + } + // Populate ancestorTypes with the path from receiverType's class to its ancestor, callTarget's declaring type. fun walkFrom(c: IrClass): Boolean { if(declaringType == c) @@ -1686,7 +1694,7 @@ open class KotlinFileExtractor( result } - private fun isFunction(target: IrFunction, pkgName: String, classNameLogged: String, classNamePredicate: (String) -> Boolean, fName: String, hasQuestionMark: Boolean? = false): Boolean { + private fun isFunction(target: IrFunction, pkgName: String, classNameLogged: String, classNamePredicate: (String) -> Boolean, fName: String, isNullable: Boolean? = false): Boolean { val verbose = false fun verboseln(s: String) { if(verbose) println(s) } verboseln("Attempting match for $pkgName $classNameLogged $fName") @@ -1696,14 +1704,14 @@ open class KotlinFileExtractor( } val extensionReceiverParameter = target.extensionReceiverParameter val targetClass = if (extensionReceiverParameter == null) { - if (hasQuestionMark == true) { + if (isNullable == true) { verboseln("Nullablility of type didn't match (target is not an extension method)") return false } target.parent } else { val st = extensionReceiverParameter.type as? IrSimpleType - if (hasQuestionMark != null && st?.hasQuestionMark != hasQuestionMark) { + if (isNullable != null && st?.isNullable() != isNullable) { verboseln("Nullablility of type didn't match") return false } @@ -1730,8 +1738,8 @@ open class KotlinFileExtractor( return true } - private fun isFunction(target: IrFunction, pkgName: String, className: String, fName: String, hasQuestionMark: Boolean? = false) = - isFunction(target, pkgName, className, { it == className }, fName, hasQuestionMark) + private fun isFunction(target: IrFunction, pkgName: String, className: String, fName: String, isNullable: Boolean? = false) = + isFunction(target, pkgName, className, { it == className }, fName, isNullable) private fun isNumericFunction(target: IrFunction, fName: String): Boolean { return isFunction(target, "kotlin", "Int", fName) || @@ -2574,8 +2582,8 @@ open class KotlinFileExtractor( indexVarDecl.initializer?.let { indexVarInitializer -> (e.statements[2] as? IrCall)?.let { arraySetCall -> if (isFunction(arraySetCall.symbol.owner, "kotlin", "(some array type)", { isArrayType(it) }, "set")) { - val updateRhs = arraySetCall.getValueArgument(1) - if (updateRhs == null) { + val updateRhs0 = arraySetCall.getValueArgument(1) + if (updateRhs0 == null) { logger.errorElement("Update RHS not found", e) return false } @@ -2588,7 +2596,7 @@ open class KotlinFileExtractor( receiverVal -> receiverVal.symbol.owner == arrayVarDecl.symbol.owner } ?: false }, - updateRhs + updateRhs0 )?.let { updateRhs -> val origin = e.origin if (origin == null) { @@ -3097,7 +3105,14 @@ open class KotlinFileExtractor( extractTypeOperatorCall(e, callable, exprParent.parent, exprParent.idx, exprParent.enclosingStmt) } is IrVararg -> { - logger.errorElement("Unexpected IrVararg", e) + var spread = e.elements.getOrNull(0) as? IrSpreadElement + if (spread == null || e.elements.size != 1) { + logger.errorElement("Unexpected IrVararg", e) + return + } + // There are lowered IR cases when the vararg expression is not within a call, such as + // val temp0 = [*expr] + extractExpression(spread.expression, callable, parent) } is IrGetObjectValue -> { // For `object MyObject { ... }`, the .class has an @@ -3406,15 +3421,11 @@ open class KotlinFileExtractor( data class ReceiverInfo(val receiver: IrExpression, val type: IrType, val field: Label, val indexOffset: Int) - private fun makeReceiverInfo(callableReferenceExpr: IrCallableReference, receiver: IrExpression?, indexOffset: Int): ReceiverInfo? { + private fun makeReceiverInfo(receiver: IrExpression?, indexOffset: Int): ReceiverInfo? { if (receiver == null) { return null } val type = receiver.type - if (type == null) { - logger.warnElement("Receiver has no type", callableReferenceExpr) - return null - } val field: Label = tw.getFreshIdLabel() return ReceiverInfo(receiver, type, field, indexOffset) } @@ -3427,8 +3438,8 @@ open class KotlinFileExtractor( : GeneratedClassHelper(locId, ids) { // Only one of the receivers can be non-null, but we defensively handle the case when both are null anyway - private val dispatchReceiverInfo = makeReceiverInfo(callableReferenceExpr, callableReferenceExpr.dispatchReceiver, 0) - private val extensionReceiverInfo = makeReceiverInfo(callableReferenceExpr, callableReferenceExpr.extensionReceiver, if (dispatchReceiverInfo == null) 0 else 1) + private val dispatchReceiverInfo = makeReceiverInfo(callableReferenceExpr.dispatchReceiver, 0) + private val extensionReceiverInfo = makeReceiverInfo(callableReferenceExpr.extensionReceiver, if (dispatchReceiverInfo == null) 0 else 1) fun extractReceiverField() { val firstAssignmentStmtIdx = 1 diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt index 02c2f88c73b..8a3dd214837 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinUsesExtractor.kt @@ -327,7 +327,7 @@ open class KotlinUsesExtractor( return f return globalExtensionState.syntheticToRealFunctionMap.getOrPut(f) { val result = replacementClass.declarations.findSubType { replacementDecl -> - replacementDecl is IrSimpleFunction && replacementDecl.name == f.name && replacementDecl.valueParameters.size == f.valueParameters.size && replacementDecl.valueParameters.zip(f.valueParameters).all { + replacementDecl.name == f.name && replacementDecl.valueParameters.size == f.valueParameters.size && replacementDecl.valueParameters.zip(f.valueParameters).all { erase(it.first.type) == erase(it.second.type) } } @@ -650,7 +650,7 @@ open class KotlinUsesExtractor( otherIsPrimitive: Boolean, javaClass: IrClass, kotlinPackageName: String, kotlinClassName: String): TypeResults { - val javaResult = if ((context == TypeContext.RETURN || (context == TypeContext.OTHER && otherIsPrimitive)) && !s.hasQuestionMark && primitiveName != null) { + val javaResult = if ((context == TypeContext.RETURN || (context == TypeContext.OTHER && otherIsPrimitive)) && !s.isNullable() && primitiveName != null) { val label: Label = tw.getLabelFor("@\"type;$primitiveName\"", { tw.writePrimitives(it, primitiveName) }) @@ -660,7 +660,7 @@ open class KotlinUsesExtractor( } val kotlinClassId = useClassInstance(kotlinClass, listOf()).typeResult.id val kotlinResult = if (true) TypeResult(fakeKotlinType(), "TODO", "TODO") else - if (s.hasQuestionMark) { + if (s.isNullable()) { val kotlinSignature = "$kotlinPackageName.$kotlinClassName?" // TODO: Is this right? val kotlinLabel = "@\"kt_type;nullable;$kotlinPackageName.$kotlinClassName\"" val kotlinId: Label = tw.getLabelFor(kotlinLabel, { @@ -704,13 +704,13 @@ open class KotlinUsesExtractor( owner is IrClass -> { val args = if (s.isRawType()) null else s.arguments - return useSimpleTypeClass(owner, args, s.hasQuestionMark) + return useSimpleTypeClass(owner, args, s.isNullable()) } owner is IrTypeParameter -> { val javaResult = useTypeParameter(owner) val aClassId = makeClass("kotlin", "TypeParam") // TODO: Wrong val kotlinResult = if (true) TypeResult(fakeKotlinType(), "TODO", "TODO") else - if (s.hasQuestionMark) { + if (s.isNullable()) { val kotlinSignature = "${javaResult.signature}?" // TODO: Wrong val kotlinLabel = "@\"kt_type;nullable;type_param\"" // TODO: Wrong val kotlinId: Label = tw.getLabelFor(kotlinLabel, { @@ -1485,7 +1485,7 @@ open class KotlinUsesExtractor( if (t.isArray() || t.isNullableArray()) { val elementType = t.getArrayElementType(pluginContext.irBuiltIns) val erasedElementType = erase(elementType) - return owner.typeWith(erasedElementType).codeQlWithHasQuestionMark(t.hasQuestionMark) + return owner.typeWith(erasedElementType).codeQlWithHasQuestionMark(t.isNullable()) } return if (t.arguments.isNotEmpty()) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt index 38358d6fbd1..b402eee0030 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/TypeSubstitution.kt @@ -56,7 +56,7 @@ private fun IrSimpleType.substituteTypeArguments(substitutionMap: Map this is IrTypeProjection -> this.type.let { when(it) { - is IrSimpleType -> if (it.hasQuestionMark == b) this else makeTypeProjection(it.codeQlWithHasQuestionMark(b), this.variance) + is IrSimpleType -> if (it.isNullable() == b) this else makeTypeProjection(it.codeQlWithHasQuestionMark(b), this.variance) else -> this }} else -> this diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/build.gradle b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/build.gradle new file mode 100644 index 00000000000..c0bc3340524 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/build.gradle @@ -0,0 +1,25 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.0.2/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id 'org.jetbrains.kotlin.jvm' version '1.7.0' + + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +application { + // Define the main class for the application. + mainClass = 'testProject.AppKt' +} diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/src/main/kotlin/testProject/App.kt b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/src/main/kotlin/testProject/App.kt new file mode 100644 index 00000000000..0ed9df24a57 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/app/src/main/kotlin/testProject/App.kt @@ -0,0 +1,15 @@ +/* + * This Kotlin source file was generated by the Gradle 'init' task. + */ +package testProject + +class App { + val greeting: String + get() { + return "Hello World!" + } +} + +fun main() { + // TODO: println(App().greeting) +} diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.expected b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.expected new file mode 100644 index 00000000000..d560b9b520d --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.expected @@ -0,0 +1,15 @@ +| 0 | -Xallow-no-source-files | +| 1 | -classpath | +| 2 | | +| 3 | -d | +| 4 | app/build/classes/kotlin/main | +| 5 | -jdk-home | +| 6 | | +| 7 | -module-name | +| 8 | app | +| 9 | -no-reflect | +| 10 | -no-stdlib | +| 11 | | +| 12 | app/src/main/kotlin/testProject/App.kt | +| 13 | -jvm-target | +| 14 | 1.8 | diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.ql b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.ql new file mode 100644 index 00000000000..fe4bedba634 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/compArgs.ql @@ -0,0 +1,15 @@ +import java + +private string getArgument(Compilation c, int i) { + exists(string arg | arg = c.getArgument(i) | + if exists(arg.indexOf("-Xplugin=")) + then result = "" + else + if c.getArgument(i - 1) = ["-classpath", "-jdk-home"] + then result = "" + else result = arg + ) +} + +from Compilation c, int i +select i, getArgument(c, i) diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/settings.gradle b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/settings.gradle new file mode 100644 index 00000000000..a56fb7dd11c --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.0.2/userguide/multi_project_builds.html + */ + +rootProject.name = 'testProject' +include('app') diff --git a/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/test.py b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/test.py new file mode 100644 index 00000000000..e5e9aca69bc --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/compiler_arguments/test.py @@ -0,0 +1,5 @@ +from create_database_utils import * + +run_codeql_database_create( + ["gradle build --no-daemon --no-build-cache"], lang="java") +runSuccessfully(["gradle", "clean"]) diff --git a/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/build.gradle b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/build.gradle new file mode 100644 index 00000000000..924524190cf --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/build.gradle @@ -0,0 +1,25 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Kotlin application project to get you started. + * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle + * User Manual available at https://docs.gradle.org/7.0.2/userguide/building_java_projects.html + */ + +plugins { + // Apply the org.jetbrains.kotlin.jvm Plugin to add support for Kotlin. + id 'org.jetbrains.kotlin.jvm' version '1.6.20' + + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +application { + // Define the main class for the application. + mainClass = 'testProject.AppKt' +} diff --git a/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/src/main/kotlin/testProject/App.kt b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/src/main/kotlin/testProject/App.kt new file mode 100644 index 00000000000..283f9270626 --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/app/src/main/kotlin/testProject/App.kt @@ -0,0 +1,10 @@ +import kotlin.reflect.* + +fun fn() { + val ref: KFunction2 = Ccc::m + ref.invoke(Ccc(), 1) +} + +class Ccc { + fun m(i:Int):Double = 5.0 +} diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.expected b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/diagnostics.expected similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.expected rename to java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/diagnostics.expected diff --git a/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/diagnostics.ql b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/diagnostics.ql new file mode 100644 index 00000000000..071cbb1b6ec --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/diagnostics.ql @@ -0,0 +1,4 @@ +import java +import semmle.code.java.Diagnostics + +query predicate diag(Diagnostic d) { any() } diff --git a/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/settings.gradle b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/settings.gradle new file mode 100644 index 00000000000..a56fb7dd11c --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/settings.gradle @@ -0,0 +1,11 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user manual at https://docs.gradle.org/7.0.2/userguide/multi_project_builds.html + */ + +rootProject.name = 'testProject' +include('app') diff --git a/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/test.py b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/test.py new file mode 100644 index 00000000000..3a4f9d2032e --- /dev/null +++ b/java/ql/integration-tests/posix-only/kotlin/kotlin_kfunction/test.py @@ -0,0 +1,5 @@ +from create_database_utils import * + +run_codeql_database_create( + ["gradle build --no-daemon --no-build-cache --rerun-tasks"], lang="java") +runSuccessfully(["gradle", "clean"]) diff --git a/java/ql/lib/change-notes/2022-08-18-android-manifest-backup-predicate.md b/java/ql/lib/change-notes/2022-08-18-android-manifest-backup-predicate.md new file mode 100644 index 00000000000..0af4f964170 --- /dev/null +++ b/java/ql/lib/change-notes/2022-08-18-android-manifest-backup-predicate.md @@ -0,0 +1,4 @@ +--- +category: feature +--- +* Added a new predicate, `allowsBackup`, in the `AndroidApplicationXmlElement` class. This predicate detects if the application element does not disable the `android:allowBackup` attribute. diff --git a/java/ql/lib/change-notes/2022-09-06-notificationcompat-summaries.md b/java/ql/lib/change-notes/2022-09-06-notificationcompat-summaries.md new file mode 100644 index 00000000000..e95ad457844 --- /dev/null +++ b/java/ql/lib/change-notes/2022-09-06-notificationcompat-summaries.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added new flow steps for `androidx.core.app.NotificationCompat` and its inner classes. + \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-09-07-implicit-pendingintents-compat-sinks.md b/java/ql/lib/change-notes/2022-09-07-implicit-pendingintents-compat-sinks.md new file mode 100644 index 00000000000..a1ae10ac7d5 --- /dev/null +++ b/java/ql/lib/change-notes/2022-09-07-implicit-pendingintents-compat-sinks.md @@ -0,0 +1,5 @@ +--- +category: minorAnalysis +--- +* Added new sinks to the query `java/android/implict-pendingintents` to take into account the classes `androidx.core.app.NotificationManagerCompat` and `androidx.core.app.AlarmManagerCompat`. + \ No newline at end of file diff --git a/java/ql/lib/change-notes/2022-09-13-Member-getQualifiedName.md b/java/ql/lib/change-notes/2022-09-13-Member-getQualifiedName.md new file mode 100644 index 00000000000..70b897911a8 --- /dev/null +++ b/java/ql/lib/change-notes/2022-09-13-Member-getQualifiedName.md @@ -0,0 +1,4 @@ +--- +category: breaking +--- +* The `Member.getQualifiedName()` predicate result now includes the qualified name of the declaring type. diff --git a/java/ql/lib/change-notes/2022-09-16-dispatch-confidence.md b/java/ql/lib/change-notes/2022-09-16-dispatch-confidence.md new file mode 100644 index 00000000000..9287e9abae3 --- /dev/null +++ b/java/ql/lib/change-notes/2022-09-16-dispatch-confidence.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* The virtual dispatch relation used in data flow now favors summary models over source code for dispatch to interface methods from `java.util` unless there is evidence that a specific source implementation is reachable. This should provide increased precision for any projects that include, for example, custom `List` or `Map` implementations. diff --git a/java/ql/lib/change-notes/released/0.3.4.md b/java/ql/lib/change-notes/released/0.3.4.md index 3fcd4f17053..4270abf9774 100644 --- a/java/ql/lib/change-notes/released/0.3.4.md +++ b/java/ql/lib/change-notes/released/0.3.4.md @@ -19,7 +19,7 @@ ### Minor Analysis Improvements -* Added new flow steps for the classes `java.io.Path` and `java.nio.Paths`. +* Added new flow steps for the classes `java.nio.file.Path` and `java.nio.file.Paths`. * The class `AndroidFragment` now also models the Android Jetpack version of the `Fragment` class (`androidx.fragment.app.Fragment`). * Java 19 builds can now be extracted. There are no non-preview new language features in this release, so the only user-visible change is that the CodeQL extractor will now correctly trace compilations using the JDK 19 release of `javac`. * Classes and methods that are seen with several different paths during the extraction process (for example, packaged into different JAR files) now report an arbitrarily selected location via their `getLocation` and `hasLocationInfo` predicates, rather than reporting all of them. This may lead to reduced alert duplication. diff --git a/java/ql/lib/semmle/code/java/Member.qll b/java/ql/lib/semmle/code/java/Member.qll index ef3546b0af8..456e6e8b12a 100644 --- a/java/ql/lib/semmle/code/java/Member.qll +++ b/java/ql/lib/semmle/code/java/Member.qll @@ -20,8 +20,14 @@ class Member extends Element, Annotatable, Modifiable, @member { /** Gets the type in which this member is declared. */ RefType getDeclaringType() { declaresMember(result, this) } - /** Gets the qualified name of this member. */ - string getQualifiedName() { result = this.getDeclaringType().getName() + "." + this.getName() } + /** + * Gets the qualified name of this member. + * This is useful for debugging, but for normal use `hasQualifiedName` + * is recommended, as it is more efficient. + */ + string getQualifiedName() { + result = this.getDeclaringType().getQualifiedName() + "." + this.getName() + } /** * Holds if this member has the specified name and is declared in the diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index d5b66baafb2..c3fd63cbf26 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -287,10 +287,12 @@ private module RankEdge implements RankedEdge { } private signature module TypePropagation { - predicate candType(TypeFlowNode n, RefType t); + class Typ; + + predicate candType(TypeFlowNode n, Typ t); bindingset[t] - predicate supportsType(TypeFlowNode n, RefType t); + predicate supportsType(TypeFlowNode n, Typ t); } /** Implements recursion through `forall` by way of edge ranking. */ @@ -300,7 +302,7 @@ private module ForAll { * thus is a candidate bound for `n`. */ pragma[nomagic] - private predicate candJoinType(TypeFlowNode n, RefType t) { + private predicate candJoinType(TypeFlowNode n, T::Typ t) { exists(TypeFlowNode mid | T::candType(mid, t) and Edge::edgeRank(_, mid, n) @@ -311,7 +313,7 @@ private module ForAll { * Holds if `t` is a candidate bound for `n` that is also valid for data coming * through the edges into `n` ranked from `1` to `r`. */ - private predicate flowJoin(int r, TypeFlowNode n, RefType t) { + private predicate flowJoin(int r, TypeFlowNode n, T::Typ t) { ( r = 1 and candJoinType(n, t) or @@ -325,7 +327,7 @@ private module ForAll { * coming through all the incoming edges, and therefore is a valid bound for * `n`. */ - predicate flowJoin(TypeFlowNode n, RefType t) { flowJoin(Edge::lastRank(n), n, t) } + predicate flowJoin(TypeFlowNode n, T::Typ t) { flowJoin(Edge::lastRank(n), n, t) } } module RankedJoinStep = RankEdge; @@ -337,11 +339,13 @@ private predicate exactTypeBase(TypeFlowNode n, RefType t) { n.asExpr() = e and e.getType() = t and not e instanceof FunctionalExpr and - exists(RefType sub | sub.getASourceSupertype() = t.getSourceDeclaration()) + exists(SrcRefType sub | sub.getASourceSupertype() = t.getSourceDeclaration()) ) } private module ExactTypePropagation implements TypePropagation { + class Typ = RefType; + predicate candType = exactType/2; predicate supportsType = exactType/2; @@ -384,17 +388,23 @@ private predicate upcastCand(TypeFlowNode n, RefType t1, RefType t1e, RefType t2 ) } +private predicate unconstrained(BoundedType t) { + t.(Wildcard).isUnconstrained() + or + t.getUpperBoundType() instanceof TypeObject and + not t.(Wildcard).hasLowerBound() + or + unconstrained(t.getUpperBoundType()) + or + unconstrained(t.(Wildcard).getLowerBoundType()) +} + /** Holds if `t` is a raw type or parameterised type with unrestricted type arguments. */ private predicate unbound(RefType t) { t instanceof RawType or exists(ParameterizedType pt | pt = t | - forex(RefType arg | arg = pt.getATypeArgument() | - arg.(Wildcard).isUnconstrained() - or - arg.(BoundedType).getUpperBoundType() instanceof TypeObject and - not arg.(Wildcard).hasLowerBound() - ) + forex(RefType arg | arg = pt.getATypeArgument() | unconstrained(arg)) ) } @@ -492,9 +502,10 @@ predicate arrayInstanceOfGuarded(ArrayAccess aa, RefType t) { /** * Holds if `n` has type `t` and this information is discarded, such that `t` - * might be a better type bound for nodes where `n` flows to. + * might be a better type bound for nodes where `n` flows to. This might include + * multiple bounds for a single node. */ -private predicate typeFlowBase(TypeFlowNode n, RefType t) { +private predicate typeFlowBaseCand(TypeFlowNode n, RefType t) { exists(RefType srctype | upcast(n, srctype) or upcastEnhancedForStmt(n.asSsa(), srctype) or @@ -509,7 +520,29 @@ private predicate typeFlowBase(TypeFlowNode n, RefType t) { ) } +/** + * Holds if `n` has type `t` and this information is discarded, such that `t` + * might be a better type bound for nodes where `n` flows to. This only includes + * the best such bound for each node. + */ +private predicate typeFlowBase(TypeFlowNode n, RefType t) { + exists(RefType te | + typeFlowBaseCand(n, t) and + te = t.getErasure() and + not exists(RefType better | + typeFlowBaseCand(n, better) and + better != t and + not t.getASupertype+() = better + | + better.getASupertype+() = t or + better.getErasure().(RefType).getASourceSupertype+() = te + ) + ) +} + private module TypeFlowPropagation implements TypePropagation { + class Typ = RefType; + predicate candType = typeFlow/2; bindingset[t] @@ -589,6 +622,175 @@ private predicate bestTypeFlow(TypeFlowNode n, RefType t) { not irrelevantBound(n, t) } +private predicate bestTypeFlow(TypeFlowNode n, RefType t, boolean exact) { + exactType(n, t) and exact = true + or + not exactType(n, _) and bestTypeFlow(n, t) and exact = false +} + +private predicate bestTypeFlowOrTypeFlowBase(TypeFlowNode n, RefType t, boolean exact) { + bestTypeFlow(n, t, exact) + or + typeFlowBase(n, t) and + exact = false and + not bestTypeFlow(n, _, _) +} + +/** + * Holds if `n` has type `t` and this information is not propagated as a + * universal bound to a subsequent node, such that `t` might form the basis for + * a union type bound for that node. + */ +private predicate unionTypeFlowBaseCand(TypeFlowNode n, RefType t, boolean exact) { + exists(TypeFlowNode next | + joinStep(n, next) and + bestTypeFlowOrTypeFlowBase(n, t, exact) and + not bestTypeFlowOrTypeFlowBase(next, t, exact) and + not exactType(next, _) + ) +} + +/** + * Holds if `ioe` checks `v`, its true-successor is `bb`, and `bb` has multiple + * predecessors. + */ +private predicate instanceofDisjunct(InstanceOfExpr ioe, BasicBlock bb, BaseSsaVariable v) { + ioe.getExpr() = v.getAUse() and + strictcount(bb.getABBPredecessor()) > 1 and + exists(ConditionBlock cb | cb.getCondition() = ioe and cb.getTestSuccessor(true) = bb) +} + +/** Holds if `bb` is disjunctively guarded by multiple `instanceof` tests on `v`. */ +private predicate instanceofDisjunction(BasicBlock bb, BaseSsaVariable v) { + strictcount(InstanceOfExpr ioe | instanceofDisjunct(ioe, bb, v)) = + strictcount(bb.getABBPredecessor()) +} + +/** + * Holds if `n` is a value that is guarded by a disjunction of + * `instanceof t_i` where `t` is one of those `t_i`. + */ +private predicate instanceofDisjunctionGuarded(TypeFlowNode n, RefType t) { + exists(BasicBlock bb, InstanceOfExpr ioe, BaseSsaVariable v, VarAccess va | + instanceofDisjunction(bb, v) and + bb.bbDominates(va.getBasicBlock()) and + va = v.getAUse() and + instanceofDisjunct(ioe, bb, v) and + t = ioe.getCheckedType() and + n.asExpr() = va + ) +} + +private module HasUnionTypePropagation implements TypePropagation { + class Typ = Unit; + + predicate candType(TypeFlowNode mid, Unit unit) { + exists(unit) and + (unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid)) + } + + predicate supportsType = candType/2; +} + +/** + * Holds if all incoming type flow can be traced back to a + * `unionTypeFlowBaseCand`, such that we can compute a union type bound for `n`. + * Disregards nodes for which we have an exact bound. + */ +private predicate hasUnionTypeFlow(TypeFlowNode n) { + not exactType(n, _) and + ( + // Optimized version of + // `forex(TypeFlowNode mid | joinStep(mid, n) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))` + ForAll::flowJoin(n, _) + or + exists(TypeFlowNode scc | + sccRepr(n, scc) and + // Optimized version of + // `forex(TypeFlowNode mid | sccJoinStep(mid, scc) | unionTypeFlowBaseCand(mid, _, _) or hasUnionTypeFlow(mid))` + ForAll::flowJoin(scc, _) + ) + or + exists(TypeFlowNode mid | step(mid, n) and hasUnionTypeFlow(mid)) + or + instanceofDisjunctionGuarded(n, _) + ) +} + +pragma[nomagic] +private RefType getTypeBound(TypeFlowNode n) { + bestTypeFlow(n, result) + or + not bestTypeFlow(n, _) and result = n.getType() +} + +pragma[nomagic] +private predicate unionTypeFlow0(TypeFlowNode n, RefType t, boolean exact) { + hasUnionTypeFlow(n) and + ( + exists(TypeFlowNode mid | anyStep(mid, n) | + unionTypeFlowBaseCand(mid, t, exact) or unionTypeFlow(mid, t, exact) + ) + or + instanceofDisjunctionGuarded(n, t) and exact = false + ) +} + +/** Holds if we have a union type bound for `n` and `t` is one of its parts. */ +private predicate unionTypeFlow(TypeFlowNode n, RefType t, boolean exact) { + unionTypeFlow0(n, t, exact) and + // filter impossible union parts: + if exact = true + then t.getErasure().(RefType).getASourceSupertype*() = getTypeBound(n).getErasure() + else haveIntersection(getTypeBound(n), t) +} + +/** + * Holds if the inferred union type bound for `n` contains the best universal + * bound and thus is irrelevant. + */ +private predicate irrelevantUnionType(TypeFlowNode n) { + exists(RefType t, RefType nt, RefType te, RefType nte | + unionTypeFlow(n, t, false) and + nt = getTypeBound(n) and + te = t.getErasure() and + nte = nt.getErasure() + | + nt.getASupertype*() = t + or + nte.getASourceSupertype+() = te + or + nte = te and unbound(t) + ) +} + +/** + * Holds if `t` is an irrelevant part of the union type bound for `n` due to + * being contained in another part of the union type bound. + */ +private predicate irrelevantUnionTypePart(TypeFlowNode n, RefType t, boolean exact) { + unionTypeFlow(n, t, exact) and + not irrelevantUnionType(n) and + exists(RefType weaker | + unionTypeFlow(n, weaker, false) and + t.getASupertype*() = weaker + | + exact = true or not weaker.getASupertype*() = t + ) +} + +/** + * Holds if the runtime type of `n` is bounded by a union type and if this + * bound is likely to be better than the static type of `n`. The union type is + * made up of the types `t` related to `n` by this predicate, and the flag + * `exact` indicates whether `t` is an exact bound or merely an upper bound. + */ +private predicate bestUnionType(TypeFlowNode n, RefType t, boolean exact) { + unionTypeFlow(n, t, exact) and + not irrelevantUnionType(n) and + not irrelevantUnionTypePart(n, t, exact) +} + cached private module TypeFlowBounds { /** @@ -600,11 +802,7 @@ private module TypeFlowBounds { predicate fieldTypeFlow(Field f, RefType t, boolean exact) { exists(TypeFlowNode n | n.asField() = f and - ( - exactType(n, t) and exact = true - or - not exactType(n, _) and bestTypeFlow(n, t) and exact = false - ) + bestTypeFlow(n, t, exact) ) } @@ -617,11 +815,21 @@ private module TypeFlowBounds { predicate exprTypeFlow(Expr e, RefType t, boolean exact) { exists(TypeFlowNode n | n.asExpr() = e and - ( - exactType(n, t) and exact = true - or - not exactType(n, _) and bestTypeFlow(n, t) and exact = false - ) + bestTypeFlow(n, t, exact) + ) + } + + /** + * Holds if the runtime type of `e` is bounded by a union type and if this + * bound is likely to be better than the static type of `e`. The union type is + * made up of the types `t` related to `e` by this predicate, and the flag + * `exact` indicates whether `t` is an exact bound or merely an upper bound. + */ + cached + predicate exprUnionTypeFlow(Expr e, RefType t, boolean exact) { + exists(TypeFlowNode n | + n.asExpr() = e and + bestUnionType(n, t, exact) ) } } diff --git a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll index 3eedae0159b..a57d1ca32be 100644 --- a/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll +++ b/java/ql/lib/semmle/code/java/dataflow/internal/DataFlowDispatch.qll @@ -4,12 +4,31 @@ private import DataFlowUtil private import semmle.code.java.dataflow.InstanceAccess private import semmle.code.java.dataflow.FlowSummary private import semmle.code.java.dispatch.VirtualDispatch as VirtualDispatch +private import semmle.code.java.dataflow.TypeFlow private import semmle.code.java.dispatch.internal.Unification private module DispatchImpl { + private predicate hasHighConfidenceTarget(Call c) { + exists(SummarizedCallable sc | + sc = c.getCallee().getSourceDeclaration() and not sc.isAutoGenerated() + ) + or + exists(Callable srcTgt | + srcTgt = VirtualDispatch::viableCallable(c) and + not VirtualDispatch::lowConfidenceDispatchTarget(c, srcTgt) + ) + } + + private Callable sourceDispatch(Call c) { + result = VirtualDispatch::viableCallable(c) and + if VirtualDispatch::lowConfidenceDispatchTarget(c, result) + then not hasHighConfidenceTarget(c) + else any() + } + /** Gets a viable implementation of the target of the given `Call`. */ DataFlowCallable viableCallable(DataFlowCall c) { - result.asCallable() = VirtualDispatch::viableCallable(c.asCall()) + result.asCallable() = sourceDispatch(c.asCall()) or result.asSummarizedCallable() = c.asCall().getCallee().getSourceDeclaration() } @@ -21,7 +40,7 @@ private module DispatchImpl { */ private predicate mayBenefitFromCallContext(MethodAccess ma, Callable c, int i) { exists(Parameter p | - 2 <= strictcount(VirtualDispatch::viableImpl(ma)) and + 2 <= strictcount(sourceDispatch(ma)) and ma.getQualifier().(VarAccess).getVariable() = p and p.getPosition() = i and c.getAParameter() = p and @@ -30,7 +49,7 @@ private module DispatchImpl { ) or exists(OwnInstanceAccess ia | - 2 <= strictcount(VirtualDispatch::viableImpl(ma)) and + 2 <= strictcount(sourceDispatch(ma)) and (ia.isExplicit(ma.getQualifier()) or ia.isImplicitMethodQualifier(ma)) and i = -1 and c = ma.getEnclosingCallable() @@ -46,7 +65,7 @@ private module DispatchImpl { private predicate relevantContext(Call ctx, int i) { exists(Callable c | mayBenefitFromCallContext(_, c, i) and - c = VirtualDispatch::viableCallable(ctx) + c = sourceDispatch(ctx) ) } @@ -63,15 +82,21 @@ private module DispatchImpl { private predicate contextArgHasType(Call ctx, int i, RefType t, boolean exact) { relevantContext(ctx, i) and exists(RefType srctype | - exists(Expr arg, Expr src | + exists(Expr arg | i = -1 and ctx.getQualifier() = arg or ctx.getArgument(i) = arg | - src = VirtualDispatch::variableTrack(arg) and - srctype = getPreciseType(src) and - if src instanceof ClassInstanceExpr then exact = true else exact = false + exprTypeFlow(arg, srctype, exact) + or + not exprTypeFlow(arg, _, _) and + exprUnionTypeFlow(arg, srctype, exact) + or + not exprTypeFlow(arg, _, _) and + not exprUnionTypeFlow(arg, _, _) and + srctype = getPreciseType(arg) and + if arg instanceof ClassInstanceExpr then exact = true else exact = false ) or exists(Node arg | diff --git a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll index 52b1b34dc35..56385e89877 100644 --- a/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll +++ b/java/ql/lib/semmle/code/java/dispatch/VirtualDispatch.qll @@ -54,6 +54,16 @@ private module Dispatch { cached Method viableImpl(MethodAccess ma) { result = ObjFlow::viableImpl_out(ma) } + /** + * Holds if `m` is a viable implementation of the method called in `ma` for + * which we only have imprecise open-world type-based dispatch resolution, and + * the dispatch type is likely to yield implausible dispatch targets. + */ + cached + predicate lowConfidenceDispatchTarget(MethodAccess ma, Method m) { + m = viableImpl(ma) and lowConfidenceDispatch(ma) + } + /** * INTERNAL: Use `viableImpl` instead. * @@ -62,8 +72,42 @@ private module Dispatch { cached Method viableImpl_v3(MethodAccess ma) { result = DispatchFlow::viableImpl_out(ma) } - private predicate qualType(VirtualMethodAccess ma, RefType t, boolean exact) { - exprTypeFlow(ma.getQualifier(), t, exact) + /** + * Holds if the best type bounds for the qualifier of `ma` are likely to + * contain implausible dispatch targets. + */ + private predicate lowConfidenceDispatch(VirtualMethodAccess ma) { + exists(RefType t | hasQualifierType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) and + ( + not qualType(ma, _, _) + or + exists(RefType t | qualType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) + ) and + ( + not qualUnionType(ma, _, _) + or + exists(RefType t | qualUnionType(ma, t, false) | + lowConfidenceDispatchType(t.getSourceDeclaration()) + ) + ) + } + + private predicate lowConfidenceDispatchType(SrcRefType t) { + t instanceof TypeObject + or + t instanceof FunctionalInterface + or + t.hasQualifiedName("java.io", "Serializable") + or + t.hasQualifiedName("java.lang", "Cloneable") + or + t.getPackage().hasName("java.util") and t instanceof Interface + or + t.hasQualifiedName("java.util", "Hashtable") } /** @@ -73,6 +117,35 @@ private module Dispatch { */ cached Method viableImpl_v2(MethodAccess ma) { + result = viableImpl_v2_cand(pragma[only_bind_into](ma)) and + exists(Method def, RefType t, boolean exact | + qualUnionType(pragma[only_bind_into](ma), pragma[only_bind_into](t), + pragma[only_bind_into](exact)) and + def = ma.getMethod().getSourceDeclaration() + | + exact = true and result = exactMethodImpl(def, t.getSourceDeclaration()) + or + exact = false and + exists(RefType t2 | + result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and + not Unification_v2::failsUnification(t, t2) + ) + ) + or + result = viableImpl_v2_cand(ma) and + not qualUnionType(ma, _, _) + } + + private predicate qualUnionType(VirtualMethodAccess ma, RefType t, boolean exact) { + exprUnionTypeFlow(ma.getQualifier(), t, exact) + } + + private predicate unificationTargetLeft_v2(ParameterizedType t1) { qualUnionType(_, t1, _) } + + private module Unification_v2 = + MkUnification; + + private Method viableImpl_v2_cand(MethodAccess ma) { result = viableImpl_v1(ma) and ( exists(Method def, RefType t, boolean exact | @@ -84,7 +157,7 @@ private module Dispatch { exact = false and exists(RefType t2 | result = viableMethodImpl(def, t.getSourceDeclaration(), t2) and - not Unification_v2::failsUnification(t, t2) + not Unification_v2_cand::failsUnification(t, t2) ) ) or @@ -92,10 +165,14 @@ private module Dispatch { ) } - private predicate unificationTargetLeft_v2(ParameterizedType t1) { qualType(_, t1, _) } + private predicate qualType(VirtualMethodAccess ma, RefType t, boolean exact) { + exprTypeFlow(ma.getQualifier(), t, exact) + } - private module Unification_v2 = - MkUnification; + private predicate unificationTargetLeft_v2_cand(ParameterizedType t1) { qualType(_, t1, _) } + + private module Unification_v2_cand = + MkUnification; /** * INTERNAL: Use `viableImpl` instead. @@ -161,9 +238,8 @@ private module Dispatch { } private predicate hasQualifierType(VirtualMethodAccess ma, RefType t, boolean exact) { - exists(Expr src | src = variableTrack(ma.getQualifier()) | - // If we have a qualifier, then we track it through variable assignments - // and take the type of the assigned value. + exists(Expr src | src = ma.getQualifier() | + // If we have a qualifier, then we take its type. exists(RefType srctype | srctype = getPreciseType(src) | exists(BoundedType bd | bd = srctype | t = bd.getAnUltimateUpperBoundType() @@ -224,34 +300,7 @@ private module Dispatch { import Dispatch -private Expr variableTrackStep(Expr use) { - exists(Variable v | - pragma[only_bind_out](use) = v.getAnAccess() and - use.getType() instanceof RefType and - not result instanceof NullLiteral and - not v.(LocalVariableDecl).getDeclExpr().hasImplicitInit() - | - not v instanceof Parameter and - result = v.getAnAssignedValue() - or - exists(Parameter p | p = v and p.getCallable().isPrivate() | - result = p.getAnAssignedValue() or - result = p.getAnArgument() - ) - ) -} - -private Expr variableTrackPath(Expr use) { - result = variableTrackStep*(use) and - not exists(variableTrackStep(result)) -} - /** - * Gets an expression by tracking `use` backwards through variable assignments. + * DEPRECATED: Use `TypeFlow` instead. */ -pragma[inline] -Expr variableTrack(Expr use) { - result = variableTrackPath(use) - or - not exists(variableTrackPath(use)) and result = use -} +deprecated Expr variableTrack(Expr use) { result = use } diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Notifications.qll b/java/ql/lib/semmle/code/java/frameworks/android/Notifications.qll index f16af6dcf89..0f69f0bbe1d 100644 --- a/java/ql/lib/semmle/code/java/frameworks/android/Notifications.qll +++ b/java/ql/lib/semmle/code/java/frameworks/android/Notifications.qll @@ -19,6 +19,17 @@ private class NotificationBuildersSummaryModels extends SummaryModelCsv { "android.app;Notification$Action$Builder;true;build;;;Argument[-1];ReturnValue;taint;manual", "android.app;Notification$Action$Builder;true;build;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue.SyntheticField[android.content.Intent.extras];value;manual", "android.app;Notification$Action$Builder;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual", + "androidx.core.app;NotificationCompat$Action;true;Action;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Action;true;Action;(IconCompat,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Action;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(IconCompat,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(Action);;Argument[0];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;addExtras;;;Argument[0].MapKey;Argument[-1].SyntheticField[android.content.Intent.extras].MapKey;value;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;addExtras;;;Argument[0].MapValue;Argument[-1].SyntheticField[android.content.Intent.extras].MapValue;value;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;build;;;Argument[-1];ReturnValue;taint;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;build;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue.SyntheticField[android.content.Intent.extras];value;manual", + "androidx.core.app;NotificationCompat$Action$Builder;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual", "android.app;Notification$Builder;true;addAction;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", "android.app;Notification$Builder;true;addAction;(Action);;Argument[0];Argument[-1];taint;manual", "android.app;Notification$Builder;true;addExtras;;;Argument[0].MapKey;Argument[-1].SyntheticField[android.content.Intent.extras].MapKey;value;manual", @@ -32,18 +43,30 @@ private class NotificationBuildersSummaryModels extends SummaryModelCsv { "android.app;Notification$Builder;true;setExtras;;;Argument[0];Argument[-1].SyntheticField[android.content.Intent.extras];value;manual", "android.app;Notification$Builder;true;setDeleteIntent;;;Argument[0];Argument[-1];taint;manual", "android.app;Notification$Builder;true;setPublicVersion;;;Argument[0];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;addAction;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;addAction;(Action);;Argument[0];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;addExtras;;;Argument[0].MapKey;Argument[-1].SyntheticField[android.content.Intent.extras].MapKey;value;manual", + "androidx.core.app;NotificationCompat$Builder;true;addExtras;;;Argument[0].MapValue;Argument[-1].SyntheticField[android.content.Intent.extras].MapValue;value;manual", + "androidx.core.app;NotificationCompat$Builder;true;build;;;Argument[-1];ReturnValue;taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;build;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue.Field[android.app.Notification.extras];value;manual", + "androidx.core.app;NotificationCompat$Builder;true;setContentIntent;;;Argument[0];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual", + "androidx.core.app;NotificationCompat$Builder;true;setExtras;;;Argument[0];Argument[-1].SyntheticField[android.content.Intent.extras];value;manual", + "androidx.core.app;NotificationCompat$Builder;true;setDeleteIntent;;;Argument[0];Argument[-1];taint;manual", + "androidx.core.app;NotificationCompat$Builder;true;setPublicVersion;;;Argument[0];Argument[-1];taint;manual", "android.app;Notification$Style;true;build;;;Argument[-1];ReturnValue;taint;manual", "android.app;Notification$BigPictureStyle;true;BigPictureStyle;(Builder);;Argument[0];Argument[-1];taint;manual", "android.app;Notification$BigTextStyle;true;BigTextStyle;(Builder);;Argument[0];Argument[-1];taint;manual", "android.app;Notification$InboxStyle;true;InboxStyle;(Builder);;Argument[0];Argument[-1];taint;manual", "android.app;Notification$MediaStyle;true;MediaStyle;(Builder);;Argument[0];Argument[-1];taint;manual", // Fluent models - "android.app;Notification$Action$Builder;true;" + + ["android.app;Notification", "androidx.core.app;NotificationCompat"] + + "$Action$Builder;true;" + [ "addExtras", "addRemoteInput", "extend", "setAllowGeneratedReplies", "setAuthenticationRequired", "setContextual", "setSemanticAction" ] + ";;;Argument[-1];ReturnValue;value;manual", - "android.app;Notification$Builder;true;" + + ["android.app;Notification", "androidx.core.app;NotificationCompat"] + "$Builder;true;" + [ "addAction", "addExtras", "addPerson", "extend", "setActions", "setAutoCancel", "setBadgeIconType", "setBubbleMetadata", "setCategory", "setChannelId", @@ -58,15 +81,16 @@ private class NotificationBuildersSummaryModels extends SummaryModelCsv { "setSubText", "setTicker", "setTimeoutAfter", "setUsesChronometer", "setVibrate", "setVisibility", "setWhen" ] + ";;;Argument[-1];ReturnValue;value;manual", - "android.app;Notification$BigPictureStyle;true;" + + ["android.app;Notification", "androidx.core.app;NotificationCompat"] + + "$BigPictureStyle;true;" + [ "bigLargeIcon", "bigPicture", "setBigContentTitle", "setContentDescription", "setSummaryText", "showBigPictureWhenCollapsed" ] + ";;;Argument[-1];ReturnValue;value;manual", - "android.app;Notification$BigTextStyle;true;" + - ["bigText", "setBigContentTitle", "setSummaryText"] + + ["android.app;Notification", "androidx.core.app;NotificationCompat"] + "$BigTextStyle;true;" + + ["bigText", "setBigContentTitle", "setSummaryText"] + ";;;Argument[-1];ReturnValue;value;manual", - "android.app;Notification$InboxStyle;true;" + + ["android.app;Notification", "androidx.core.app;NotificationCompat"] + "$InboxStyle;true;" + ["addLine", "setBigContentTitle", "setSummaryText"] + ";;;Argument[-1];ReturnValue;value;manual", "android.app;Notification$MediaStyle;true;" + diff --git a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll index 74d8c2e1577..ccd4e5a757a 100644 --- a/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll +++ b/java/ql/lib/semmle/code/java/security/ExternalAPIs.qll @@ -24,7 +24,7 @@ private class DefaultSafeExternalApiMethod extends SafeExternalApiMethod { or this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "Validate") or - this.getQualifiedName() = "Objects.equals" + this.hasQualifiedName("java.util", "Objects", "equals") or this.getDeclaringType() instanceof TypeString and this.getName() = "equals" or @@ -92,10 +92,7 @@ class ExternalApiDataNode extends DataFlow::Node { int getIndex() { result = i } /** Gets the description of the method being called. */ - string getMethodDescription() { - result = - this.getMethod().getDeclaringType().getPackage() + "." + this.getMethod().getQualifiedName() - } + string getMethodDescription() { result = this.getMethod().getQualifiedName() } } /** DEPRECATED: Alias for ExternalApiDataNode */ diff --git a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll index 540b28445e5..3441cfaef18 100644 --- a/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll +++ b/java/ql/lib/semmle/code/java/security/ImplicitPendingIntents.qll @@ -102,6 +102,8 @@ private class PendingIntentSentSinkModels extends SinkModelCsv { "android.app;NotificationManager;true;notify;(String,int,Notification);;Argument[2];pending-intent-sent;manual", "android.app;NotificationManager;true;notifyAsPackage;(String,String,int,Notification);;Argument[3];pending-intent-sent;manual", "android.app;NotificationManager;true;notifyAsUser;(String,int,Notification,UserHandle);;Argument[2];pending-intent-sent;manual", + "androidx.core.app;NotificationManagerCompat;true;notify;(int,Notification);;Argument[1];pending-intent-sent;manual", + "androidx.core.app;NotificationManagerCompat;true;notify;(String,int,Notification);;Argument[2];pending-intent-sent;manual", "android.app;PendingIntent;false;send;(Context,int,Intent,OnFinished,Handler,String,Bundle);;Argument[2];pending-intent-sent;manual", "android.app;PendingIntent;false;send;(Context,int,Intent,OnFinished,Handler,String);;Argument[2];pending-intent-sent;manual", "android.app;PendingIntent;false;send;(Context,int,Intent,OnFinished,Handler);;Argument[2];pending-intent-sent;manual", @@ -115,6 +117,10 @@ private class PendingIntentSentSinkModels extends SinkModelCsv { "android.app;AlarmManager;true;setInexactRepeating;;;Argument[3];pending-intent-sent;manual", "android.app;AlarmManager;true;setRepeating;;;Argument[3];pending-intent-sent;manual", "android.app;AlarmManager;true;setWindow;(int,long,long,PendingIntent);;Argument[3];pending-intent-sent;manual", + "androidx.core.app;AlarmManagerCompat;true;setAlarmClock;;;Argument[2..3];pending-intent-sent;manual", + "androidx.core.app;AlarmManagerCompat;true;setAndAllowWhileIdle;;;Argument[3];pending-intent-sent;manual", + "androidx.core.app;AlarmManagerCompat;true;setExact;;;Argument[3];pending-intent-sent;manual", + "androidx.core.app;AlarmManagerCompat;true;setExactAndAllowWhileIdle;;;Argument[3];pending-intent-sent;manual", ] } } diff --git a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll index e11bc5182f0..65e662f0bc5 100644 --- a/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll +++ b/java/ql/lib/semmle/code/java/security/OverlyLargeRangeQuery.qll @@ -96,7 +96,10 @@ class OverlyWideRange extends RegExpCharacterRange { toCodePoint("A") <= high or // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) + exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and + // while still being ascii + low < 128 and + high < 128 ) and // allowlist for known ranges not this = allowedWideRanges() diff --git a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll index 033b8aa8cfd..5112bdad11e 100644 --- a/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll +++ b/java/ql/lib/semmle/code/java/security/regexp/NfaUtils.qll @@ -887,11 +887,10 @@ module PrefixConstruction { /** * Holds if `state` is the textually last start state for the regular expression. */ - private predicate lastStartState(State state) { + private predicate lastStartState(RelevantState state) { exists(RegExpRoot root | state = - max(State s, Location l | - s = stateInRelevantRegexp() and + max(RelevantState s, Location l | isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() @@ -963,10 +962,17 @@ module PrefixConstruction { min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) } - /** Gets a state within a regular expression that contains a candidate state. */ - pragma[noinline] - State stateInRelevantRegexp() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(result.getRepr())) + /** A state within a regular expression that contains a candidate state. */ + class RelevantState instanceof State { + RelevantState() { + exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + + /** Gets a string representation for this state in a regular expression. */ + string toString() { result = State.super.toString() } + + /** Gets the term represented by this state. */ + RegExpTerm getRepr() { result = State.super.getRepr() } } } @@ -1007,6 +1013,8 @@ module ReDoSPruning { import PrefixConstruction as Prefix + class RelevantState = Prefix::RelevantState; + /** * Predicates for testing the presence of a rejecting suffix. * @@ -1040,32 +1048,26 @@ module ReDoSPruning { * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. */ pragma[noinline] - private predicate isLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and - ( - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - ) + private predicate isLikelyRejectable(RelevantState s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) } /** * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. */ - predicate isRejectState(State s) { - s = Prefix::stateInRelevantRegexp() and not epsilonSucc*(s) = Accept(_) - } + predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } /** * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. */ pragma[noopt] - predicate hasEdgeToLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and + predicate hasEdgeToLikelyRejectable(RelevantState s) { // all edges (at least one) with some char leads to another state that is rejectable. // the `next` states might not share a common suffix, which can cause FPs. exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | @@ -1080,8 +1082,7 @@ module ReDoSPruning { * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. */ pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(State s) { - s = Prefix::stateInRelevantRegexp() and + private string hasEdgeToLikelyRejectableHelper(RelevantState s) { not hasRejectEdge(s) and not isRejectState(s) and deltaClosedChar(s, result, _) @@ -1092,9 +1093,7 @@ module ReDoSPruning { * along epsilon edges, such that there is a transition from * `prev` to `next` that the character symbol `char`. */ - predicate deltaClosedChar(State prev, string char, State next) { - prev = Prefix::stateInRelevantRegexp() and - next = Prefix::stateInRelevantRegexp() and + predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) } diff --git a/java/ql/lib/semmle/code/xml/AndroidManifest.qll b/java/ql/lib/semmle/code/xml/AndroidManifest.qll index b5c79efa853..f53da67a650 100644 --- a/java/ql/lib/semmle/code/xml/AndroidManifest.qll +++ b/java/ql/lib/semmle/code/xml/AndroidManifest.qll @@ -72,6 +72,56 @@ class AndroidApplicationXmlElement extends XmlElement { * Holds if this application element has explicitly set a value for its `android:permission` attribute. */ predicate requiresPermissions() { this.getAnAttribute().(AndroidPermissionXmlAttribute).isFull() } + + /** + * Holds if this application element does not disable the `android:allowBackup` attribute. + * + * https://developer.android.com/guide/topics/data/autobackup + */ + predicate allowsBackup() { + not this.getFile().(AndroidManifestXmlFile).isInBuildDirectory() and + ( + // explicitly sets android:allowBackup="true" + this.allowsBackupExplicitly() + or + // Manifest providing the main intent for an application, and does not explicitly + // disallow the allowBackup attribute + this.providesMainIntent() and + // Check that android:allowBackup="false" is not present + not exists(AndroidXmlAttribute attr | + this.getAnAttribute() = attr and + attr.getName() = "allowBackup" and + attr.getValue() = "false" + ) + ) + } + + /** + * Holds if this application element sets the `android:allowBackup` attribute to `true`. + * + * https://developer.android.com/guide/topics/data/autobackup + */ + private predicate allowsBackupExplicitly() { + exists(AndroidXmlAttribute attr | + this.getAnAttribute() = attr and + attr.getName() = "allowBackup" and + attr.getValue() = "true" + ) + } + + /** + * Holds if the application element contains a child element which provides the + * `android.intent.action.MAIN` intent. + */ + private predicate providesMainIntent() { + exists(AndroidActivityXmlElement activity | + activity = this.getAChild() and + exists(AndroidIntentFilterXmlElement intentFilter | + intentFilter = activity.getAChild() and + intentFilter.getAnActionElement().getActionName() = "android.intent.action.MAIN" + ) + ) + } } /** diff --git a/java/ql/src/Advisory/Deprecated Code/AvoidDeprecatedCallableAccess.ql b/java/ql/src/Advisory/Deprecated Code/AvoidDeprecatedCallableAccess.ql index 354b82fe165..f20be6d7f34 100644 --- a/java/ql/src/Advisory/Deprecated Code/AvoidDeprecatedCallableAccess.ql +++ b/java/ql/src/Advisory/Deprecated Code/AvoidDeprecatedCallableAccess.ql @@ -20,7 +20,7 @@ private predicate isDeprecatedCallable(Callable c) { from Call ca, Callable c where - ca.getCallee() = c and + ca.getCallee().getSourceDeclaration() = c and isDeprecatedCallable(c) and // Exclude deprecated calls from within deprecated code. not isDeprecatedCallable(ca.getCaller()) diff --git a/java/ql/src/Architecture/Refactoring Opportunities/HubClasses.ql b/java/ql/src/Architecture/Refactoring Opportunities/HubClasses.ql index d50e1d3b5d9..97101414527 100644 --- a/java/ql/src/Architecture/Refactoring Opportunities/HubClasses.ql +++ b/java/ql/src/Architecture/Refactoring Opportunities/HubClasses.ql @@ -19,6 +19,6 @@ where eff = t.getMetrics().getEfferentSourceCoupling() and aff > 15 and eff > 15 -select t as Class, +select t as class_, "Hub class: this class depends on " + eff.toString() + " classes and is used by " + aff.toString() + " classes." diff --git a/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql b/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql index b350eecae7f..46b7c599f24 100644 --- a/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql +++ b/java/ql/src/Likely Bugs/Collections/IteratorRemoveMayFail.ql @@ -36,11 +36,11 @@ predicate containsSpecialCollection(Expr e, SpecialCollectionCreation origin) { or exists(Call c, int i | containsSpecialCollection(c.getArgument(i), origin) and - e = c.getCallee().getParameter(i).getAnAccess() + e = c.getCallee().getSourceDeclaration().getParameter(i).getAnAccess() ) or exists(Call c, ReturnStmt r | e = c | - r.getEnclosingCallable() = c.getCallee() and + r.getEnclosingCallable() = c.getCallee().getSourceDeclaration() and containsSpecialCollection(r.getResult(), origin) ) } @@ -58,11 +58,11 @@ predicate iterOfSpecialCollection(Expr e, SpecialCollectionCreation origin) { or exists(Call c, int i | iterOfSpecialCollection(c.getArgument(i), origin) and - e = c.getCallee().getParameter(i).getAnAccess() + e = c.getCallee().getSourceDeclaration().getParameter(i).getAnAccess() ) or exists(Call c, ReturnStmt r | e = c | - r.getEnclosingCallable() = c.getCallee() and + r.getEnclosingCallable() = c.getCallee().getSourceDeclaration() and iterOfSpecialCollection(r.getResult(), origin) ) } diff --git a/java/ql/src/Performance/InnerClassCouldBeStatic.ql b/java/ql/src/Performance/InnerClassCouldBeStatic.ql index 02ca5544da2..67ee835b747 100644 --- a/java/ql/src/Performance/InnerClassCouldBeStatic.ql +++ b/java/ql/src/Performance/InnerClassCouldBeStatic.ql @@ -78,7 +78,7 @@ RefType enclosingInstanceAccess(Expr expr) { result = ma.getMethod().getDeclaringType() and not exists(ma.getQualifier()) and not ma.getMethod().isStatic() and - not exists(Method m | m.getSourceDeclaration() = ma.getMethod() | enclosing.inherits(m)) + not enclosing.inherits(ma.getMethod()) ) ) } diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql index 671e9b00b4d..c9ea50c6f29 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPath.ql @@ -48,6 +48,10 @@ class TaintedPathConfig extends TaintTracking::Configuration { or node = DataFlow::BarrierGuard::getABarrierNode() } + + override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + any(TaintedPathAdditionalTaintStep s).step(n1, n2) + } } /** diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll b/java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll index beea2d5f666..0e826a6fc01 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPathCommon.qll @@ -5,6 +5,49 @@ import java import semmle.code.java.controlflow.Guards import semmle.code.java.security.PathCreation +import semmle.code.java.frameworks.Networking +import semmle.code.java.dataflow.DataFlow + +/** + * A unit class for adding additional taint steps. + * + * Extend this class to add additional taint steps that should apply to tainted path flow configurations. + */ +class TaintedPathAdditionalTaintStep extends Unit { + abstract predicate step(DataFlow::Node n1, DataFlow::Node n2); +} + +private class DefaultTaintedPathAdditionalTaintStep extends TaintedPathAdditionalTaintStep { + override predicate step(DataFlow::Node n1, DataFlow::Node n2) { + exists(Argument a | + a = n1.asExpr() and + a.getCall() = n2.asExpr() and + a = any(TaintPreservingUriCtorParam tpp).getAnArgument() + ) + } +} + +private class TaintPreservingUriCtorParam extends Parameter { + TaintPreservingUriCtorParam() { + exists(Constructor ctor, int idx, int nParams | + ctor.getDeclaringType() instanceof TypeUri and + this = ctor.getParameter(idx) and + nParams = ctor.getNumberOfParameters() + | + // URI(String scheme, String ssp, String fragment) + idx = 1 and nParams = 3 + or + // URI(String scheme, String host, String path, String fragment) + idx = [1, 2] and nParams = 4 + or + // URI(String scheme, String authority, String path, String query, String fragment) + idx = 2 and nParams = 5 + or + // URI(String scheme, String userInfo, String host, int port, String path, String query, String fragment) + idx = 4 and nParams = 7 + ) + } +} private predicate inWeakCheck(Expr e) { // None of these are sufficient to guarantee that a string is safe. diff --git a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql index ebd9c4f079d..9f138d138a6 100644 --- a/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql +++ b/java/ql/src/Security/CWE/CWE-022/TaintedPathLocal.ql @@ -27,6 +27,10 @@ class TaintedPathLocalConfig extends TaintTracking::Configuration { override predicate isSink(DataFlow::Node sink) { sink.asExpr() = any(PathCreation p).getAnInput() } + + override predicate isAdditionalTaintStep(DataFlow::Node n1, DataFlow::Node n2) { + any(TaintedPathAdditionalTaintStep s).step(n1, n2) + } } from diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.java b/java/ql/src/Security/CWE/CWE-079/XSS.java index 273da84901c..8b9a26825bc 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSS.java +++ b/java/ql/src/Security/CWE/CWE-079/XSS.java @@ -1,8 +1,9 @@ public class XSS extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { - // BAD: a request parameter is written directly to an error response page - response.sendError(HttpServletResponse.SC_NOT_FOUND, + // BAD: a request parameter is written directly to the Servlet response stream + response.getWriter().print( "The page \"" + request.getParameter("page") + "\" was not found."); + } } diff --git a/java/ql/src/Security/CWE/CWE-079/XSS.qhelp b/java/ql/src/Security/CWE/CWE-079/XSS.qhelp index 428b44c8db9..05b0f268002 100644 --- a/java/ql/src/Security/CWE/CWE-079/XSS.qhelp +++ b/java/ql/src/Security/CWE/CWE-079/XSS.qhelp @@ -18,7 +18,7 @@ reference.

    -

    The following example shows the page parameter being written directly to the server error page, +

    The following example shows the page parameter being written directly to the page, leaving the website vulnerable to cross-site scripting.

    diff --git a/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.qhelp b/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.qhelp new file mode 100644 index 00000000000..4f57a2659f0 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.qhelp @@ -0,0 +1,45 @@ + + + +

    In the Android manifest file, you can use the android:allowBackup attribute of the application element to define whether the +application will have automatic backups or not.

    + +

    If your application uses any sensitive data, you should disable automatic backups to prevent attackers from extracting it.

    +
    + + +

    For Android applications which process sensitive data, set android:allowBackup to false in the manifest +file.

    + +

    Note: Since Android 6.0 (Marshmallow), automatic backups for applications are switched on by default. +

    +
    + + + +

    In the following two (bad) examples, the android:allowBackup setting is enabled:

    + + + + + +

    In the following (good) example, android:allowBackup is set to false:

    + + + +
    + +
  • + Android Documentation: + Back up user data with Auto Backup +
  • +
  • + OWASP Mobile Security Testing Guide: + + Android Backups + +
  • +
    +
    \ No newline at end of file diff --git a/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.ql b/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.ql new file mode 100644 index 00000000000..d2810d70250 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/AllowBackupAttributeEnabled.ql @@ -0,0 +1,18 @@ +/** + * @name Application backup allowed + * @description Allowing application backups may allow an attacker to extract sensitive data. + * @kind problem + * @problem.severity recommendation + * @security-severity 7.5 + * @id java/android/backup-enabled + * @tags security + * external/cwe/cwe-312 + * @precision very-high + */ + +import java +import semmle.code.xml.AndroidManifest + +from AndroidApplicationXmlElement androidAppElem +where androidAppElem.allowsBackup() +select androidAppElem, "Backups are allowed in this Android application." diff --git a/java/ql/src/Security/CWE/CWE-312/AllowBackupEmpty.xml b/java/ql/src/Security/CWE/CWE-312/AllowBackupEmpty.xml new file mode 100644 index 00000000000..e3e9159b726 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/AllowBackupEmpty.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/ql/src/Security/CWE/CWE-312/AllowBackupFalse.xml b/java/ql/src/Security/CWE/CWE-312/AllowBackupFalse.xml new file mode 100644 index 00000000000..cf0a90bb60c --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/AllowBackupFalse.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/java/ql/src/Security/CWE/CWE-312/AllowBackupTrue.xml b/java/ql/src/Security/CWE/CWE-312/AllowBackupTrue.xml new file mode 100644 index 00000000000..bdec443a6c5 --- /dev/null +++ b/java/ql/src/Security/CWE/CWE-312/AllowBackupTrue.xml @@ -0,0 +1,8 @@ + + + + + + + diff --git a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql index e187320ba5d..49de716b1dc 100644 --- a/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql +++ b/java/ql/src/Security/CWE/CWE-327/MaybeBrokenCryptoAlgorithm.ql @@ -38,7 +38,7 @@ predicate objectToString(MethodAccess ma) { exists(ToStringMethod m | m = ma.getMethod() and m.getDeclaringType() instanceof TypeObject and - variableTrack(ma.getQualifier()).getType().getErasure() instanceof TypeObject + exprNode(ma.getQualifier()).getTypeBound().getErasure() instanceof TypeObject ) } diff --git a/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql b/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql index 817634f7d2c..8fe3d4e6ba6 100644 --- a/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql +++ b/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql @@ -72,7 +72,7 @@ predicate mayWriteToArray(Expr modified) { // return __array__; ... method()[1] = 0 exists(ReturnStmt rs | modified = rs.getResult() and relevantType(modified.getType()) | exists(Callable enclosing, MethodAccess ma | - enclosing = rs.getEnclosingCallable() and ma.getMethod() = enclosing + enclosing = rs.getEnclosingCallable() and ma.getMethod().getSourceDeclaration() = enclosing | mayWriteToArray(ma) ) @@ -100,7 +100,7 @@ VarAccess varPassedInto(Callable c, int i) { predicate exposesByReturn(Callable c, Field f, Expr why, string whyText) { returnsArray(c, f) and exists(MethodAccess ma | - ma.getMethod() = c and ma.getCompilationUnit() != c.getCompilationUnit() + ma.getMethod().getSourceDeclaration() = c and ma.getCompilationUnit() != c.getCompilationUnit() | mayWriteToArray(ma) and why = ma and diff --git a/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql b/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql index ec71b1207fa..f4161160303 100644 --- a/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql +++ b/java/ql/src/Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql @@ -27,7 +27,7 @@ predicate callToInheritedMethod(RefType lexicalScope, MethodAccess ma, string si not ma.getMethod().isStatic() and not ma.hasQualifier() and ma.getEnclosingCallable().getDeclaringType() = lexicalScope and - nestedSupertypePlus(lexicalScope).getAMethod() = ma.getMethod() and + nestedSupertypePlus(lexicalScope).getAMethod() = ma.getMethod().getSourceDeclaration() and signature = ma.getMethod().getSignature() } diff --git a/java/ql/src/change-notes/2022-08-18-android-allowbackup-query.md b/java/ql/src/change-notes/2022-08-18-android-allowbackup-query.md new file mode 100644 index 00000000000..e37abd0d755 --- /dev/null +++ b/java/ql/src/change-notes/2022-08-18-android-allowbackup-query.md @@ -0,0 +1,4 @@ +--- +category: newQuery +--- +* Added a new query, `java/android/backup-enabled`, to detect if Android applications allow backups. diff --git a/java/ql/src/change-notes/2022-09-13-flow-model-for-uri-constructors.md b/java/ql/src/change-notes/2022-09-13-flow-model-for-uri-constructors.md new file mode 100644 index 00000000000..254fc55d42a --- /dev/null +++ b/java/ql/src/change-notes/2022-09-13-flow-model-for-uri-constructors.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added taint model for arguments of `java.net.URI` constructors to the queries `java/path-injection` and `java/path-injection-local`. diff --git a/java/ql/src/change-notes/2022-09-15-implicit-this-generic-method.md b/java/ql/src/change-notes/2022-09-15-implicit-this-generic-method.md new file mode 100644 index 00000000000..be30188b05d --- /dev/null +++ b/java/ql/src/change-notes/2022-09-15-implicit-this-generic-method.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* The Java extractor now populates the `Method` relating to a `MethodAccess` consistently for calls using an explicit and implicit `this` qualifier. Previously if the method `foo` was inherited from a specialised generic type `ParentType`, then an explicit call `this.foo()` would yield a `MethodAccess` whose `getMethod()` accessor returned the bound method `ParentType.foo`, whereas an implicitly-qualified `foo()` `MethodAccess`'s `getMethod()` would return the unbound method `ParentType.foo`. Now both scenarios produce a bound method. This means that all data-flow queries may return more results where a relevant path transits a call to such an implicitly-qualified call to a member method with a bound generic type, while queries that inspect the result of `MethodAccess.getMethod()` may need to tolerate bound generic methods in more circumstances. The queries `java/iterator-remove-failure`, `java/non-static-nested-class`, `java/internal-representation-exposure`, `java/subtle-inherited-call` and `java/deprecated-call` have been amended to properly handle calls to bound generic methods, and in some instances may now produce more results in the explicit-`this` case as well. diff --git a/java/ql/test/kotlin/library-tests/data-classes/callees.expected b/java/ql/test/kotlin/library-tests/data-classes/callees.expected index c4b66f2d3a0..4c01e2402fa 100644 --- a/java/ql/test/kotlin/library-tests/data-classes/callees.expected +++ b/java/ql/test/kotlin/library-tests/data-classes/callees.expected @@ -1,8 +1,8 @@ -| dc.kt:0:0:0:0 | hashCode(...) | Arrays.hashCode | -| dc.kt:0:0:0:0 | hashCode(...) | Arrays.hashCode | +| dc.kt:0:0:0:0 | hashCode(...) | java.util.Arrays.hashCode | +| dc.kt:0:0:0:0 | hashCode(...) | java.util.Arrays.hashCode | | dc.kt:0:0:0:0 | new ProtoMapValue(...) | ProtoMapValue.ProtoMapValue | -| dc.kt:0:0:0:0 | plus(...) | Int.plus | -| dc.kt:0:0:0:0 | times(...) | Int.times | -| dc.kt:0:0:0:0 | toString(...) | Arrays.toString | -| dc.kt:0:0:0:0 | toString(...) | Arrays.toString | -| dc.kt:1:1:1:71 | super(...) | Object.Object | +| dc.kt:0:0:0:0 | plus(...) | kotlin.Int.plus | +| dc.kt:0:0:0:0 | times(...) | kotlin.Int.times | +| dc.kt:0:0:0:0 | toString(...) | java.util.Arrays.toString | +| dc.kt:0:0:0:0 | toString(...) | java.util.Arrays.toString | +| dc.kt:1:1:1:71 | super(...) | java.lang.Object.Object | diff --git a/java/ql/test/kotlin/library-tests/generics-location/locations.expected b/java/ql/test/kotlin/library-tests/generics-location/locations.expected index c0c37948cb6..bafdc26e216 100644 --- a/java/ql/test/kotlin/library-tests/generics-location/locations.expected +++ b/java/ql/test/kotlin/library-tests/generics-location/locations.expected @@ -12,15 +12,15 @@ classLocations | main.B | file:///!unknown-binary-location/main/B.class:0:0:0:0 | file:///!unknown-binary-location/main/B.class:0:0:0:0 | | main.B | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | callableLocations -| A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | -| A.fn | A.java:4:17:4:18 | A.java:4:17:4:18 | -| A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | -| A.fn | file:///!unknown-binary-location/main/A.class:0:0:0:0 | file:///!unknown-binary-location/main/A.class:0:0:0:0 | -| A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | -| A.fn | file:///!unknown-binary-location/main/A.class:0:0:0:0 | file:///!unknown-binary-location/main/A.class:0:0:0:0 | -| B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | -| B.fn | generics.kt:4:5:10:5 | generics.kt:4:5:10:5 | -| B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | -| B.fn | file:///!unknown-binary-location/main/B.class:0:0:0:0 | file:///!unknown-binary-location/main/B.class:0:0:0:0 | -| B.fn | file:///!unknown-binary-location/main/B.class:0:0:0:0 | file:///!unknown-binary-location/main/B.class:0:0:0:0 | -| B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | +| main.A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | +| main.A.fn | A.java:4:17:4:18 | A.java:4:17:4:18 | +| main.A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | +| main.A.fn | file:///!unknown-binary-location/main/A.class:0:0:0:0 | file:///!unknown-binary-location/main/A.class:0:0:0:0 | +| main.A.fn | A.class:0:0:0:0 | A.class:0:0:0:0 | +| main.A.fn | file:///!unknown-binary-location/main/A.class:0:0:0:0 | file:///!unknown-binary-location/main/A.class:0:0:0:0 | +| main.B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | +| main.B.fn | generics.kt:4:5:10:5 | generics.kt:4:5:10:5 | +| main.B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | +| main.B.fn | file:///!unknown-binary-location/main/B.class:0:0:0:0 | file:///!unknown-binary-location/main/B.class:0:0:0:0 | +| main.B.fn | file:///!unknown-binary-location/main/B.class:0:0:0:0 | file:///!unknown-binary-location/main/B.class:0:0:0:0 | +| main.B.fn | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | generics-location.testproj/test.class.files/main/B.class:0:0:0:0 | diff --git a/java/ql/test/kotlin/library-tests/methods-mixed-java-and-kotlin/test.expected b/java/ql/test/kotlin/library-tests/methods-mixed-java-and-kotlin/test.expected index dc6cb870289..12654f3142a 100644 --- a/java/ql/test/kotlin/library-tests/methods-mixed-java-and-kotlin/test.expected +++ b/java/ql/test/kotlin/library-tests/methods-mixed-java-and-kotlin/test.expected @@ -1,6 +1,6 @@ -| A.class:0:0:0:0 | foo | A.foo | foo(java.lang.String) | A.java:4:10:4:12 | foo | A.foo | -| A.java:4:10:4:12 | foo | A.foo | foo(java.lang.Object) | A.java:4:10:4:12 | foo | A.foo | -| B.java:4:17:4:19 | foo | B.foo | foo(java.lang.String) | B.java:4:17:4:19 | foo | B.foo | -| W.kt:4:5:4:17 | foo | A.foo | foo(java.lang.Object) | W.kt:4:5:4:17 | foo | A.foo | -| W.kt:8:14:8:34 | foo | B.foo | foo(java.lang.String) | W.kt:8:14:8:34 | foo | B.foo | -| file:///!unknown-binary-location/k/A.class:0:0:0:0 | foo | A.foo | foo(java.lang.String) | W.kt:4:5:4:17 | foo | A.foo | +| A.class:0:0:0:0 | foo | j.A.foo | foo(java.lang.String) | A.java:4:10:4:12 | foo | j.A.foo | +| A.java:4:10:4:12 | foo | j.A.foo | foo(java.lang.Object) | A.java:4:10:4:12 | foo | j.A.foo | +| B.java:4:17:4:19 | foo | j.B.foo | foo(java.lang.String) | B.java:4:17:4:19 | foo | j.B.foo | +| W.kt:4:5:4:17 | foo | k.A.foo | foo(java.lang.Object) | W.kt:4:5:4:17 | foo | k.A.foo | +| W.kt:8:14:8:34 | foo | k.B.foo | foo(java.lang.String) | W.kt:8:14:8:34 | foo | k.B.foo | +| file:///!unknown-binary-location/k/A.class:0:0:0:0 | foo | k.A.foo | foo(java.lang.String) | W.kt:4:5:4:17 | foo | k.A.foo | diff --git a/java/ql/test/kotlin/library-tests/multiple_files/method_accesses.expected b/java/ql/test/kotlin/library-tests/multiple_files/method_accesses.expected index fdb38b16ff7..849ae5204e9 100644 --- a/java/ql/test/kotlin/library-tests/multiple_files/method_accesses.expected +++ b/java/ql/test/kotlin/library-tests/multiple_files/method_accesses.expected @@ -1,5 +1,5 @@ | file1.kt:4:18:4:23 | fun2(...) | file2.kt:3:5:3:18 | fun2 | Class2.fun2 | file2.kt:2:1:4:1 | Class2 | | file1.kt:5:9:5:14 | fun3(...) | file3.kt:5:1:6:1 | fun3 | MyJvmName.fun3 | file3.kt:0:0:0:0 | MyJvmName | | file1.kt:6:9:6:14 | fun4(...) | file4.kt:4:1:5:1 | fun4 | File4Kt.fun4 | file4.kt:0:0:0:0 | File4Kt | -| file1.kt:11:29:11:56 | toArray(...) | file:///CollectionToArray.class:0:0:0:0 | toArray | CollectionToArray.toArray | file:///CollectionToArray.class:0:0:0:0 | CollectionToArray | -| file1.kt:11:47:11:55 | listOf(...) | file:///CollectionsKt.class:0:0:0:0 | listOf | CollectionsKt.listOf | file:///CollectionsKt.class:0:0:0:0 | CollectionsKt | +| file1.kt:11:29:11:56 | toArray(...) | file:///CollectionToArray.class:0:0:0:0 | toArray | kotlin.jvm.internal.CollectionToArray.toArray | file:///CollectionToArray.class:0:0:0:0 | CollectionToArray | +| file1.kt:11:47:11:55 | listOf(...) | file:///CollectionsKt.class:0:0:0:0 | listOf | kotlin.collections.CollectionsKt.listOf | file:///CollectionsKt.class:0:0:0:0 | CollectionsKt | diff --git a/java/ql/test/kotlin/library-tests/reflection/reflection.expected b/java/ql/test/kotlin/library-tests/reflection/reflection.expected index 9b6899f4b6e..12bbfb4db98 100644 --- a/java/ql/test/kotlin/library-tests/reflection/reflection.expected +++ b/java/ql/test/kotlin/library-tests/reflection/reflection.expected @@ -73,48 +73,48 @@ propertySetReferences | reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | set | file:///Class1$Generic.class:0:0:0:0 | setP2 | | reflection.kt:109:17:109:27 | ...::... | reflection.kt:109:17:109:27 | set | reflection.kt:105:18:105:31 | setProp1 | callsInsideInvocationMethods -| reflection.kt:7:49:7:54 | ...::... | reflection.kt:7:49:7:54 | new Function2(...) { ... } | reflection.kt:7:49:7:54 | invoke | reflection.kt:7:49:7:54 | m(...) | Ccc.m | -| reflection.kt:10:38:10:42 | ...::... | reflection.kt:10:38:10:42 | new KProperty1(...) { ... } | reflection.kt:10:38:10:42 | get | reflection.kt:10:38:10:42 | getP0(...) | C.getP0 | -| reflection.kt:10:38:10:42 | ...::... | reflection.kt:10:38:10:42 | new KProperty1(...) { ... } | reflection.kt:10:38:10:42 | invoke | reflection.kt:10:38:10:42 | get(...) | .get | -| reflection.kt:14:38:14:44 | ...::... | reflection.kt:14:38:14:44 | new Function1(...) { ... } | reflection.kt:14:38:14:44 | invoke | reflection.kt:14:38:14:44 | get(...) | KProperty1.get | -| reflection.kt:15:35:15:41 | ...::... | reflection.kt:15:35:15:41 | new KProperty0(...) { ... } | reflection.kt:15:35:15:41 | get | reflection.kt:15:35:15:41 | getP0(...) | C.getP0 | -| reflection.kt:15:35:15:41 | ...::... | reflection.kt:15:35:15:41 | new KProperty0(...) { ... } | reflection.kt:15:35:15:41 | invoke | reflection.kt:15:35:15:41 | get(...) | .get | -| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | get | reflection.kt:17:45:17:49 | getP1(...) | C.getP1 | -| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | invoke | reflection.kt:17:45:17:49 | get(...) | .get | -| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | set | reflection.kt:17:45:17:49 | setP1(...) | C.setP1 | -| reflection.kt:21:44:21:50 | ...::... | reflection.kt:21:44:21:50 | new Function2(...) { ... } | reflection.kt:21:44:21:50 | invoke | reflection.kt:21:44:21:50 | set(...) | KMutableProperty1.set | -| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | get | reflection.kt:22:42:22:48 | getP1(...) | C.getP1 | -| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | invoke | reflection.kt:22:42:22:48 | get(...) | .get | -| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | set | reflection.kt:22:42:22:48 | setP1(...) | C.setP1 | +| reflection.kt:7:49:7:54 | ...::... | reflection.kt:7:49:7:54 | new Function2(...) { ... } | reflection.kt:7:49:7:54 | invoke | reflection.kt:7:49:7:54 | m(...) | Reflection$Ccc.m | +| reflection.kt:10:38:10:42 | ...::... | reflection.kt:10:38:10:42 | new KProperty1(...) { ... } | reflection.kt:10:38:10:42 | get | reflection.kt:10:38:10:42 | getP0(...) | Reflection$C.getP0 | +| reflection.kt:10:38:10:42 | ...::... | reflection.kt:10:38:10:42 | new KProperty1(...) { ... } | reflection.kt:10:38:10:42 | invoke | reflection.kt:10:38:10:42 | get(...) | .get | +| reflection.kt:14:38:14:44 | ...::... | reflection.kt:14:38:14:44 | new Function1(...) { ... } | reflection.kt:14:38:14:44 | invoke | reflection.kt:14:38:14:44 | get(...) | kotlin.reflect.KProperty1.get | +| reflection.kt:15:35:15:41 | ...::... | reflection.kt:15:35:15:41 | new KProperty0(...) { ... } | reflection.kt:15:35:15:41 | get | reflection.kt:15:35:15:41 | getP0(...) | Reflection$C.getP0 | +| reflection.kt:15:35:15:41 | ...::... | reflection.kt:15:35:15:41 | new KProperty0(...) { ... } | reflection.kt:15:35:15:41 | invoke | reflection.kt:15:35:15:41 | get(...) | .get | +| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | get | reflection.kt:17:45:17:49 | getP1(...) | Reflection$C.getP1 | +| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | invoke | reflection.kt:17:45:17:49 | get(...) | .get | +| reflection.kt:17:45:17:49 | ...::... | reflection.kt:17:45:17:49 | new KMutableProperty1(...) { ... } | reflection.kt:17:45:17:49 | set | reflection.kt:17:45:17:49 | setP1(...) | Reflection$C.setP1 | +| reflection.kt:21:44:21:50 | ...::... | reflection.kt:21:44:21:50 | new Function2(...) { ... } | reflection.kt:21:44:21:50 | invoke | reflection.kt:21:44:21:50 | set(...) | kotlin.reflect.KMutableProperty1.set | +| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | get | reflection.kt:22:42:22:48 | getP1(...) | Reflection$C.getP1 | +| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | invoke | reflection.kt:22:42:22:48 | get(...) | .get | +| reflection.kt:22:42:22:48 | ...::... | reflection.kt:22:42:22:48 | new KMutableProperty0(...) { ... } | reflection.kt:22:42:22:48 | set | reflection.kt:22:42:22:48 | setP1(...) | Reflection$C.setP1 | | reflection.kt:50:13:50:28 | ...::... | reflection.kt:50:13:50:28 | new KProperty1(...) { ... } | reflection.kt:50:13:50:28 | get | reflection.kt:50:13:50:28 | getLastChar(...) | ReflectionKt.getLastChar | -| reflection.kt:50:13:50:28 | ...::... | reflection.kt:50:13:50:28 | new KProperty1(...) { ... } | reflection.kt:50:13:50:28 | invoke | reflection.kt:50:13:50:28 | get(...) | .get | +| reflection.kt:50:13:50:28 | ...::... | reflection.kt:50:13:50:28 | new KProperty1(...) { ... } | reflection.kt:50:13:50:28 | invoke | reflection.kt:50:13:50:28 | get(...) | .get | | reflection.kt:51:13:51:28 | ...::... | reflection.kt:51:13:51:28 | new KProperty0(...) { ... } | reflection.kt:51:13:51:28 | get | reflection.kt:51:13:51:28 | getLastChar(...) | ReflectionKt.getLastChar | -| reflection.kt:51:13:51:28 | ...::... | reflection.kt:51:13:51:28 | new KProperty0(...) { ... } | reflection.kt:51:13:51:28 | invoke | reflection.kt:51:13:51:28 | get(...) | .get | -| reflection.kt:60:17:60:32 | ...::... | reflection.kt:60:17:60:32 | new Function2,Integer,String>(...) { ... } | reflection.kt:60:17:60:32 | invoke | reflection.kt:60:17:60:32 | m1(...) | Generic.m1 | -| reflection.kt:61:17:61:34 | ...::... | reflection.kt:61:17:61:34 | new Function1(...) { ... } | reflection.kt:61:17:61:34 | invoke | reflection.kt:61:17:61:34 | m1(...) | Generic.m1 | +| reflection.kt:51:13:51:28 | ...::... | reflection.kt:51:13:51:28 | new KProperty0(...) { ... } | reflection.kt:51:13:51:28 | invoke | reflection.kt:51:13:51:28 | get(...) | .get | +| reflection.kt:60:17:60:32 | ...::... | reflection.kt:60:17:60:32 | new Function2,Integer,String>(...) { ... } | reflection.kt:60:17:60:32 | invoke | reflection.kt:60:17:60:32 | m1(...) | Class1$Generic.m1 | +| reflection.kt:61:17:61:34 | ...::... | reflection.kt:61:17:61:34 | new Function1(...) { ... } | reflection.kt:61:17:61:34 | invoke | reflection.kt:61:17:61:34 | m1(...) | Class1$Generic.m1 | | reflection.kt:62:17:62:34 | ...::... | reflection.kt:62:17:62:34 | new Function1,String>(...) { ... } | reflection.kt:62:17:62:34 | invoke | reflection.kt:62:17:62:34 | ext1(...) | ReflectionKt.ext1 | | reflection.kt:63:17:63:36 | ...::... | reflection.kt:63:17:63:36 | new Function0(...) { ... } | reflection.kt:63:17:63:36 | invoke | reflection.kt:63:17:63:36 | ext1(...) | ReflectionKt.ext1 | | reflection.kt:64:17:64:34 | ...::... | reflection.kt:64:17:64:34 | new Function1,String>(...) { ... } | reflection.kt:64:17:64:34 | invoke | reflection.kt:64:17:64:34 | ext2(...) | ReflectionKt.ext2 | | reflection.kt:65:17:65:36 | ...::... | reflection.kt:65:17:65:36 | new Function0(...) { ... } | reflection.kt:65:17:65:36 | invoke | reflection.kt:65:17:65:36 | ext2(...) | ReflectionKt.ext2 | -| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | get | reflection.kt:67:17:67:32 | getP2(...) | Generic.getP2 | -| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | invoke | reflection.kt:67:17:67:32 | get(...) | .get | -| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | set | reflection.kt:67:17:67:32 | setP2(...) | Generic.setP2 | -| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | get | reflection.kt:68:17:68:34 | getP2(...) | Generic.getP2 | -| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | invoke | reflection.kt:68:17:68:34 | get(...) | .get | -| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | set | reflection.kt:68:17:68:34 | setP2(...) | Generic.setP2 | -| reflection.kt:70:17:70:30 | ...::... | reflection.kt:70:17:70:30 | new KProperty0(...) { ... } | reflection.kt:70:17:70:30 | get | reflection.kt:70:17:70:30 | getMAX_VALUE(...) | IntCompanionObject.getMAX_VALUE | -| reflection.kt:70:17:70:30 | ...::... | reflection.kt:70:17:70:30 | new KProperty0(...) { ... } | reflection.kt:70:17:70:30 | invoke | reflection.kt:70:17:70:30 | get(...) | .get | -| reflection.kt:71:17:71:34 | ...::... | reflection.kt:71:17:71:34 | new KProperty0(...) { ... } | reflection.kt:71:17:71:34 | invoke | reflection.kt:71:17:71:34 | get(...) | .get | -| reflection.kt:72:17:72:35 | ...::... | reflection.kt:72:17:72:35 | new KMutableProperty0(...) { ... } | reflection.kt:72:17:72:35 | invoke | reflection.kt:72:17:72:35 | get(...) | .get | -| reflection.kt:90:18:90:24 | ...::... | reflection.kt:90:18:90:24 | new Function1>(...) { ... } | reflection.kt:90:18:90:24 | invoke | reflection.kt:90:18:90:24 | new Inner(...) | Inner.Inner | +| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | get | reflection.kt:67:17:67:32 | getP2(...) | Class1$Generic.getP2 | +| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | invoke | reflection.kt:67:17:67:32 | get(...) | .get | +| reflection.kt:67:17:67:32 | ...::... | reflection.kt:67:17:67:32 | new KMutableProperty1,Integer>(...) { ... } | reflection.kt:67:17:67:32 | set | reflection.kt:67:17:67:32 | setP2(...) | Class1$Generic.setP2 | +| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | get | reflection.kt:68:17:68:34 | getP2(...) | Class1$Generic.getP2 | +| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | invoke | reflection.kt:68:17:68:34 | get(...) | .get | +| reflection.kt:68:17:68:34 | ...::... | reflection.kt:68:17:68:34 | new KMutableProperty0(...) { ... } | reflection.kt:68:17:68:34 | set | reflection.kt:68:17:68:34 | setP2(...) | Class1$Generic.setP2 | +| reflection.kt:70:17:70:30 | ...::... | reflection.kt:70:17:70:30 | new KProperty0(...) { ... } | reflection.kt:70:17:70:30 | get | reflection.kt:70:17:70:30 | getMAX_VALUE(...) | kotlin.jvm.internal.IntCompanionObject.getMAX_VALUE | +| reflection.kt:70:17:70:30 | ...::... | reflection.kt:70:17:70:30 | new KProperty0(...) { ... } | reflection.kt:70:17:70:30 | invoke | reflection.kt:70:17:70:30 | get(...) | .get | +| reflection.kt:71:17:71:34 | ...::... | reflection.kt:71:17:71:34 | new KProperty0(...) { ... } | reflection.kt:71:17:71:34 | invoke | reflection.kt:71:17:71:34 | get(...) | .get | +| reflection.kt:72:17:72:35 | ...::... | reflection.kt:72:17:72:35 | new KMutableProperty0(...) { ... } | reflection.kt:72:17:72:35 | invoke | reflection.kt:72:17:72:35 | get(...) | .get | +| reflection.kt:90:18:90:24 | ...::... | reflection.kt:90:18:90:24 | new Function1>(...) { ... } | reflection.kt:90:18:90:24 | invoke | reflection.kt:90:18:90:24 | new Inner(...) | Class2$Inner.Inner | | reflection.kt:97:14:97:21 | ...::... | reflection.kt:97:14:97:21 | new Function1>(...) { ... } | reflection.kt:97:14:97:21 | invoke | reflection.kt:97:14:97:21 | new Class2(...) | Class2.Class2 | | reflection.kt:98:14:98:17 | ...::... | reflection.kt:98:14:98:17 | new Function1(...) { ... } | reflection.kt:98:14:98:17 | invoke | reflection.kt:98:14:98:17 | fn(...) | ReflectionKt.fn | -| reflection.kt:99:14:99:29 | ...::... | reflection.kt:99:14:99:29 | new Function1>(...) { ... } | reflection.kt:99:14:99:29 | invoke | reflection.kt:99:14:99:29 | new Inner(...) | Inner.Inner | +| reflection.kt:99:14:99:29 | ...::... | reflection.kt:99:14:99:29 | new Function1>(...) { ... } | reflection.kt:99:14:99:29 | invoke | reflection.kt:99:14:99:29 | new Inner(...) | Class2$Inner.Inner | | reflection.kt:109:17:109:27 | ...::... | reflection.kt:109:17:109:27 | new KMutableProperty0(...) { ... } | reflection.kt:109:17:109:27 | get | reflection.kt:109:17:109:27 | getProp1(...) | Base1.getProp1 | -| reflection.kt:109:17:109:27 | ...::... | reflection.kt:109:17:109:27 | new KMutableProperty0(...) { ... } | reflection.kt:109:17:109:27 | invoke | reflection.kt:109:17:109:27 | get(...) | .get | +| reflection.kt:109:17:109:27 | ...::... | reflection.kt:109:17:109:27 | new KMutableProperty0(...) { ... } | reflection.kt:109:17:109:27 | invoke | reflection.kt:109:17:109:27 | get(...) | .get | | reflection.kt:109:17:109:27 | ...::... | reflection.kt:109:17:109:27 | new KMutableProperty0(...) { ... } | reflection.kt:109:17:109:27 | set | reflection.kt:109:17:109:27 | setProp1(...) | Base1.setProp1 | -| reflection.kt:116:40:116:44 | ...::... | reflection.kt:116:40:116:44 | new Function1(...) { ... } | reflection.kt:116:40:116:44 | invoke | reflection.kt:116:40:116:44 | fn1(...) | .fn1 | -| reflection.kt:116:40:116:44 | ...::... | reflection.kt:116:40:116:44 | new Function1(...) { ... } | reflection.kt:116:40:116:44 | invoke | reflection.kt:116:40:116:44 | new (...) | . | +| reflection.kt:116:40:116:44 | ...::... | reflection.kt:116:40:116:44 | new Function1(...) { ... } | reflection.kt:116:40:116:44 | invoke | reflection.kt:116:40:116:44 | fn1(...) | LocalFn$.fn1 | +| reflection.kt:116:40:116:44 | ...::... | reflection.kt:116:40:116:44 | new Function1(...) { ... } | reflection.kt:116:40:116:44 | invoke | reflection.kt:116:40:116:44 | new (...) | LocalFn$. | | reflection.kt:126:9:126:13 | ...::... | reflection.kt:126:9:126:13 | new Function0(...) { ... } | reflection.kt:126:9:126:13 | invoke | reflection.kt:126:9:126:13 | fn1(...) | ReflectionKt.fn1 | fieldAccessInsideInvocationMethods | reflection.kt:14:38:14:44 | ...::... | reflection.kt:14:38:14:44 | new Function1(...) { ... } | reflection.kt:14:38:14:44 | invoke | reflection.kt:14:38:14:44 | this. | diff --git a/java/ql/test/kotlin/library-tests/vararg/args.expected b/java/ql/test/kotlin/library-tests/vararg/args.expected index 3d6a2347a95..317a907bdc8 100644 --- a/java/ql/test/kotlin/library-tests/vararg/args.expected +++ b/java/ql/test/kotlin/library-tests/vararg/args.expected @@ -1,9 +1,11 @@ +diag varargsParams | test.kt:8:15:8:28 | xs | file://:0:0:0:0 | int[] | | test.kt:12:26:12:39 | xs | file://:0:0:0:0 | int[] | | test.kt:20:24:20:37 | xs | file://:0:0:0:0 | int[] | | test.kt:24:50:24:63 | xs | file://:0:0:0:0 | int[] | | test.kt:28:37:28:50 | xs | file://:0:0:0:0 | int[] | +| test.kt:50:5:50:31 | s | file://:0:0:0:0 | String[] | explicitVarargsArguments | test.kt:12:50:12:51 | xs | test.kt:12:44:12:52 | this(...) | | test.kt:38:25:38:29 | array | test.kt:38:5:38:30 | funWithOnlyVarArgs(...) | @@ -11,6 +13,7 @@ explicitVarargsArguments | test.kt:40:34:40:38 | array | test.kt:40:5:40:49 | funWithMiddleVarArgs(...) | | test.kt:44:27:44:31 | array | test.kt:44:5:44:32 | new HasVarargConstructor(...) | | test.kt:45:34:45:38 | array | test.kt:45:5:45:39 | new HasVarargConstructor(...) | +| test.kt:55:15:55:35 | tmp0_s | test.kt:55:13:55:43 | new X(...) | implicitVarargsArguments | intList.kt:3:21:3:22 | 10 | intList.kt:3:14:3:31 | listOf(...) | 0 | | intList.kt:3:25:3:26 | 11 | intList.kt:3:14:3:31 | listOf(...) | 1 | @@ -78,3 +81,6 @@ implicitVarargsArguments | test.kt:44:5:44:32 | new HasVarargConstructor(...) | 0 | test.kt:44:27:44:31 | array | | test.kt:45:5:45:39 | new HasVarargConstructor(...) | 0 | test.kt:45:27:45:29 | foo | | test.kt:45:5:45:39 | new HasVarargConstructor(...) | 1 | test.kt:45:34:45:38 | array | +| test.kt:55:13:55:43 | new X(...) | 0 | test.kt:55:42:55:42 | 1 | +| test.kt:55:13:55:43 | new X(...) | 1 | test.kt:55:15:55:35 | tmp0_s | +| test.kt:55:22:55:35 | toTypedArray(...) | 0 | test.kt:55:19:55:20 | sl | diff --git a/java/ql/test/kotlin/library-tests/vararg/args.ql b/java/ql/test/kotlin/library-tests/vararg/args.ql index e58bf622e98..4173094463b 100644 --- a/java/ql/test/kotlin/library-tests/vararg/args.ql +++ b/java/ql/test/kotlin/library-tests/vararg/args.ql @@ -1,4 +1,7 @@ import java +import semmle.code.java.Diagnostics + +query predicate diag(Diagnostic d) { d.getMessage() = "Unexpected IrVararg" } query predicate varargsParams(Parameter p, Type t) { p.getCallable().fromSource() and diff --git a/java/ql/test/kotlin/library-tests/vararg/test.kt b/java/ql/test/kotlin/library-tests/vararg/test.kt index 00dc8f13d78..966f9d0f5a9 100644 --- a/java/ql/test/kotlin/library-tests/vararg/test.kt +++ b/java/ql/test/kotlin/library-tests/vararg/test.kt @@ -1,6 +1,6 @@ fun sink(sunk: Int) { - + } open class HasVarargConstructor { @@ -44,3 +44,13 @@ fun myFun() { HasVarargConstructor(*array) HasVarargConstructor("foo", *array) } + +open class X( + i: Int, + public vararg val s: String +) { } + +fun fn(sl: List) { + // reordered args: + val x = X(s = sl.toTypedArray(), i = 1) +} diff --git a/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected index b02fa9c0e9a..3260a055b94 100644 --- a/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected +++ b/java/ql/test/library-tests/MemberRefExpr/MemberRefExpr.expected @@ -1,14 +1,14 @@ -| Test.java:24:26:24:51 | ...::... | Inner<>.Inner<> | Test$Generic$Inner.class:0:0:0:0 | Inner<> | -| Test.java:38:29:38:42 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | -| Test.java:39:29:39:42 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test | -| Test.java:40:29:40:39 | ...::... | Object.clone | Test.java:1:7:1:10 | Test | -| Test.java:41:40:41:64 | ...::... | Object.toString | Test$Generic.class:0:0:0:0 | Generic | -| Test.java:43:23:43:36 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | -| Test.java:44:23:44:36 | ...::... | Object.hashCode | Test.java:1:7:1:10 | Test | -| Test.java:45:23:45:33 | ...::... | Object.clone | Test.java:1:7:1:10 | Test | -| Test.java:48:22:48:35 | ...::... | Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:24:26:24:51 | ...::... | Test$Generic$Inner<>.Inner<> | Test$Generic$Inner.class:0:0:0:0 | Inner<> | +| Test.java:38:29:38:42 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:39:29:39:42 | ...::... | java.lang.Object.hashCode | Test.java:1:7:1:10 | Test | +| Test.java:40:29:40:39 | ...::... | java.lang.Object.clone | Test.java:1:7:1:10 | Test | +| Test.java:41:40:41:64 | ...::... | java.lang.Object.toString | Test$Generic.class:0:0:0:0 | Generic | +| Test.java:43:23:43:36 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test | +| Test.java:44:23:44:36 | ...::... | java.lang.Object.hashCode | Test.java:1:7:1:10 | Test | +| Test.java:45:23:45:33 | ...::... | java.lang.Object.clone | Test.java:1:7:1:10 | Test | +| Test.java:48:22:48:35 | ...::... | java.lang.Object.toString | Test.java:1:7:1:10 | Test | | Test.java:51:13:51:21 | ...::... | Test.Test | Test.java:1:7:1:10 | Test | -| Test.java:52:13:52:32 | ...::... | Generic.Generic | Test$Generic.class:0:0:0:0 | Generic | +| Test.java:52:13:52:32 | ...::... | Test$Generic.Generic | Test$Generic.class:0:0:0:0 | Generic | | Test.java:56:13:56:22 | ...::... | | file://:0:0:0:0 | int[] | | Test.java:57:13:57:26 | ...::... | | file://:0:0:0:0 | Generic<>[] | | Test.java:61:31:61:47 | ...::... | Test.doSomething | Test.java:1:7:1:10 | Test | diff --git a/java/ql/test/library-tests/annotation-arrays/Test.java b/java/ql/test/library-tests/annotation-arrays/Test.java new file mode 100644 index 00000000000..d7e721d8ad2 --- /dev/null +++ b/java/ql/test/library-tests/annotation-arrays/Test.java @@ -0,0 +1,14 @@ +class Test { + + @interface CustomAnnotation { + String value(); + } + + @interface ArrayValues { + CustomAnnotation[] annotationValues(); + } + + @ArrayValues(annotationValues = @CustomAnnotation(value = "only")) private int fieldWithSingular; + @ArrayValues(annotationValues = {@CustomAnnotation(value = "val1"), @CustomAnnotation(value = "val2")}) private int fieldWithMultiple; + +} diff --git a/java/ql/test/library-tests/annotation-arrays/test.expected b/java/ql/test/library-tests/annotation-arrays/test.expected new file mode 100644 index 00000000000..cd7c8dc15cc --- /dev/null +++ b/java/ql/test/library-tests/annotation-arrays/test.expected @@ -0,0 +1,3 @@ +| Test.java:11:82:11:98 | fieldWithSingular | Test.java:11:3:11:68 | ArrayValues | Test.java:11:35:11:67 | {...} | Test.java:11:35:11:67 | CustomAnnotation | +| Test.java:12:119:12:135 | fieldWithMultiple | Test.java:12:3:12:105 | ArrayValues | Test.java:12:35:12:104 | {...} | Test.java:12:36:12:68 | CustomAnnotation | +| Test.java:12:119:12:135 | fieldWithMultiple | Test.java:12:3:12:105 | ArrayValues | Test.java:12:35:12:104 | {...} | Test.java:12:71:12:103 | CustomAnnotation | diff --git a/java/ql/test/library-tests/annotation-arrays/test.ql b/java/ql/test/library-tests/annotation-arrays/test.ql new file mode 100644 index 00000000000..4c0dfd89b7b --- /dev/null +++ b/java/ql/test/library-tests/annotation-arrays/test.ql @@ -0,0 +1,9 @@ +import java + +from Field f, Annotation ann, Expr value, Expr valueChild +where + f.getDeclaringType().fromSource() and + ann = f.getAnAnnotation() and + value = ann.getAValue() and + valueChild.getParent() = value +select f, ann, value, valueChild diff --git a/java/ql/test/library-tests/dataflow/callctx/A.java b/java/ql/test/library-tests/dataflow/callctx/A.java index 3bb6382b356..99a3bed4f07 100644 --- a/java/ql/test/library-tests/dataflow/callctx/A.java +++ b/java/ql/test/library-tests/dataflow/callctx/A.java @@ -1,7 +1,7 @@ public class A { static void sink(Object x) { } - static Object source() { return null; } + static Object source(String srctag) { return null; } static class C1 { C1() { } @@ -20,7 +20,7 @@ public class A { void foo(Object x) { Object c1 = x; - sink(c1); + sink(c1); // $ hasValueFlow=c.1 hasValueFlow=c.2 hasValueFlow=c.3 hasValueFlow=C1 hasValueFlow=C1.1 hasValueFlow=C1.2 hasValueFlow=C1.3 } } @@ -33,11 +33,11 @@ public class A { void foo(Object x) { Object c2 = x; - sink(c2); + sink(c2); // $ hasValueFlow=2 hasValueFlow=c.1 hasValueFlow=c.2 hasValueFlow=c.3 hasValueFlow=C2 hasValueFlow=C2.1 hasValueFlow=C2.2 hasValueFlow=C2.3 } void callWrapFoo2() { - wrapFoo2(source()); + wrapFoo2(source("2")); } } @@ -46,18 +46,54 @@ public class A { } void test(C1 c) { - c.wrapFoo1(source()); - c.wrapFoo2(source()); - wrapFoo3(c, source()); + c.wrapFoo1(source("c.1")); + c.wrapFoo2(source("c.2")); + wrapFoo3(c, source("c.3")); - new C1(source()); - new C1().wrapFoo1(source()); - new C1().wrapFoo2(source()); - wrapFoo3(new C1(), source()); + new C1(source("C1")); + new C1().wrapFoo1(source("C1.1")); + new C1().wrapFoo2(source("C1.2")); + wrapFoo3(new C1(), source("C1.3")); - new C2(source()); - new C2().wrapFoo1(source()); - new C2().wrapFoo2(source()); - wrapFoo3(new C2(), source()); + new C2(source("C2")); + new C2().wrapFoo1(source("C2.1")); + new C2().wrapFoo2(source("C2.2")); + wrapFoo3(new C2(), source("C2.3")); + } + + static class Sup { + void wrap(Object x) { + tgt(x); + } + + void tgt(Object x) { + sink(x); // $ hasValueFlow=s + } + } + + static class A1 extends Sup { + void tgt(Object x) { + sink(x); // $ hasValueFlow=s hasValueFlow=s12 + } + } + + static class A2 extends Sup { + void tgt(Object x) { + sink(x); // $ hasValueFlow=s hasValueFlow=s12 + } + } + + static class A3 extends Sup { + void tgt(Object x) { + sink(x); // $ hasValueFlow=s + } + } + + void test2(Sup s) { + s.wrap(source("s")); + + if (s instanceof A1 || s instanceof A2) { + s.wrap(source("s12")); + } } } diff --git a/java/ql/test/library-tests/dataflow/callctx/test.expected b/java/ql/test/library-tests/dataflow/callctx/test.expected index d633eae89ce..e69de29bb2d 100644 --- a/java/ql/test/library-tests/dataflow/callctx/test.expected +++ b/java/ql/test/library-tests/dataflow/callctx/test.expected @@ -1,15 +0,0 @@ -| A.java:40:16:40:23 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:49:16:49:23 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:49:16:49:23 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:50:16:50:23 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:50:16:50:23 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:51:17:51:24 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:51:17:51:24 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:53:12:53:19 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:54:23:54:30 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:55:23:55:30 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:56:24:56:31 | source(...) | A.java:23:12:23:13 | c1 | -| A.java:58:12:58:19 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:59:23:59:30 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:60:23:60:30 | source(...) | A.java:36:12:36:13 | c2 | -| A.java:61:24:61:31 | source(...) | A.java:36:12:36:13 | c2 | diff --git a/java/ql/test/library-tests/dataflow/callctx/test.ql b/java/ql/test/library-tests/dataflow/callctx/test.ql index 36ba2f3d17b..5d91e4e8e26 100644 --- a/java/ql/test/library-tests/dataflow/callctx/test.ql +++ b/java/ql/test/library-tests/dataflow/callctx/test.ql @@ -1,15 +1,2 @@ import java -import semmle.code.java.dataflow.DataFlow -import DataFlow - -class Conf extends Configuration { - Conf() { this = "qqconf" } - - override predicate isSource(Node n) { n.asExpr().(MethodAccess).getMethod().hasName("source") } - - override predicate isSink(Node n) { n.asExpr().(Argument).getCall().getCallee().hasName("sink") } -} - -from Node src, Node sink, Conf c -where c.hasFlow(src, sink) -select src, sink +import TestUtilities.InlineFlowTest diff --git a/java/ql/test/library-tests/dispatch/ViableCallable4.java b/java/ql/test/library-tests/dispatch/ViableCallable4.java new file mode 100644 index 00000000000..bb1a5f4a678 --- /dev/null +++ b/java/ql/test/library-tests/dispatch/ViableCallable4.java @@ -0,0 +1,23 @@ +public class ViableCallable4 { + static class Sup { + void f() { } + } + static class A1 extends Sup { + @Override void f() { } + } + static class A2 extends Sup { + @Override void f() { } + } + static class A3 extends Sup { + @Override void f() { } + } + + void foo(Sup s, boolean b) { + s.f(); + if (s instanceof A1 || s instanceof A2) { + s.f(); + } + Sup s2 = b ? new A3() : new Sup(); + s2.f(); + } +} diff --git a/java/ql/test/library-tests/dispatch/viableCallable.expected b/java/ql/test/library-tests/dispatch/viableCallable.expected index 7490e4693b9..15ff247a514 100644 --- a/java/ql/test/library-tests/dispatch/viableCallable.expected +++ b/java/ql/test/library-tests/dispatch/viableCallable.expected @@ -7,6 +7,14 @@ | ViableCallable3.java:12:5:12:9 | h(...) | h | 1 | ViableCallable3 | | ViableCallable3.java:17:9:17:17 | get(...) | get | 9 | new Func(...) { ... } | | ViableCallable3.java:18:9:18:18 | get(...) | get | 10 | new Func(...) { ... } | +| ViableCallable4.java:16:5:16:9 | f(...) | f | 2 | Sup | +| ViableCallable4.java:16:5:16:9 | f(...) | f | 5 | A1 | +| ViableCallable4.java:16:5:16:9 | f(...) | f | 8 | A2 | +| ViableCallable4.java:16:5:16:9 | f(...) | f | 11 | A3 | +| ViableCallable4.java:18:7:18:11 | f(...) | f | 5 | A1 | +| ViableCallable4.java:18:7:18:11 | f(...) | f | 8 | A2 | +| ViableCallable4.java:21:5:21:10 | f(...) | f | 2 | Sup | +| ViableCallable4.java:21:5:21:10 | f(...) | f | 11 | A3 | | ViableCallable.java:5:3:5:13 | M(...) | M | 81 | C2 | | ViableCallable.java:5:3:5:13 | M(...) | M | 86 | C3 | | ViableCallable.java:5:3:5:13 | M(...) | M | 91 | C4 | diff --git a/java/ql/test/library-tests/frameworks/android/notification/Test.java b/java/ql/test/library-tests/frameworks/android/notification/Test.java index 566c52b0a6e..d669e7421bf 100644 --- a/java/ql/test/library-tests/frameworks/android/notification/Test.java +++ b/java/ql/test/library-tests/frameworks/android/notification/Test.java @@ -1,11 +1,13 @@ package generatedtest; import android.app.Notification; +import androidx.core.app.NotificationCompat; import android.app.PendingIntent; import android.app.Person; import android.app.Notification.Action; import android.graphics.Bitmap; import android.graphics.drawable.Icon; +import androidx.core.graphics.drawable.IconCompat; import android.media.AudioAttributes; import android.net.Uri; import android.os.Bundle; @@ -103,7 +105,8 @@ public class Test { } { // "android.app;Notification$Action$Builder;true;build;;;SyntheticField[android.content.Intent.extras] - // of Argument[-1];SyntheticField[android.content.Intent.extras] of ReturnValue;value;manual" + // of Argument[-1];SyntheticField[android.content.Intent.extras] of + // ReturnValue;value;manual" Notification.Action out = null; Notification.Action.Builder builder = null; Bundle in = (Bundle) newWithMapValueDefault(source()); @@ -814,6 +817,647 @@ public class Test { Notification.MediaStyle out = in.setShowActionsInCompactView(null); sink(out); // $ hasValueFlow } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(Action);;Argument[0];Argument[-1];taint;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action in = (NotificationCompat.Action) source(); + out = new NotificationCompat.Action.Builder(in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(IconCompat,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual" + NotificationCompat.Action.Builder out = null; + PendingIntent in = (PendingIntent) source(); + out = new NotificationCompat.Action.Builder((IconCompat) null, (CharSequence) null, in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;Builder;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual" + NotificationCompat.Action.Builder out = null; + PendingIntent in = (PendingIntent) source(); + out = new NotificationCompat.Action.Builder(0, (CharSequence) null, in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;addExtras;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.addExtras(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;addExtras;;;Argument[0].MapKey;Argument[-1].SyntheticField[android.content.Intent.extras].MapKey;value;manual" + NotificationCompat.Action.Builder out = null; + Bundle in = (Bundle) newWithMapKeyDefault(source()); + out.addExtras(in); + sink(getMapKeyDefault(out.getExtras())); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;addExtras;;;Argument[0].MapValue;Argument[-1].SyntheticField[android.content.Intent.extras].MapValue;value;manual" + NotificationCompat.Action.Builder out = null; + Bundle in = (Bundle) newWithMapValueDefault(source()); + out.addExtras(in); + sink(getMapValueDefault(out.getExtras())); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;addRemoteInput;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.addRemoteInput(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;build;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue.SyntheticField[android.content.Intent.extras];value;manual" + NotificationCompat.Action out = null; + NotificationCompat.Action.Builder builder = null; + Bundle in = (Bundle) newWithMapValueDefault(source()); + builder.addExtras(in); + out = builder.build(); + sink(getMapValueDefault(out.getExtras())); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;build;;;Argument[-1];ReturnValue;taint;manual" + NotificationCompat.Action out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.build(); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;extend;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.extend(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual" + Bundle out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.getExtras(); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;setAllowGeneratedReplies;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.setAllowGeneratedReplies(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;setContextual;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.setContextual(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action$Builder;true;setSemanticAction;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Action.Builder out = null; + NotificationCompat.Action.Builder in = (NotificationCompat.Action.Builder) source(); + out = in.setSemanticAction(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Action;true;Action;(IconCompat,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual" + NotificationCompat.Action out = null; + PendingIntent in = (PendingIntent) source(); + out = new NotificationCompat.Action((IconCompat) null, (CharSequence) null, in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action;true;Action;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual" + NotificationCompat.Action out = null; + PendingIntent in = (PendingIntent) source(); + out = new NotificationCompat.Action(0, (CharSequence) null, in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Action;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual" + Bundle out = null; + NotificationCompat.Action in = (NotificationCompat.Action) source(); + out = in.getExtras(); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigLargeIcon;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigPictureStyle out = null; + NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source(); + out = in.bigLargeIcon(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigPictureStyle;true;bigPicture;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigPictureStyle out = null; + NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source(); + out = in.bigPicture(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigPictureStyle;true;setBigContentTitle;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigPictureStyle out = null; + NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source(); + out = in.setBigContentTitle(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigPictureStyle;true;setSummaryText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigPictureStyle out = null; + NotificationCompat.BigPictureStyle in = (NotificationCompat.BigPictureStyle) source(); + out = in.setSummaryText(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigTextStyle;true;bigText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigTextStyle out = null; + NotificationCompat.BigTextStyle in = (NotificationCompat.BigTextStyle) source(); + out = in.bigText(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigTextStyle;true;setBigContentTitle;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigTextStyle out = null; + NotificationCompat.BigTextStyle in = (NotificationCompat.BigTextStyle) source(); + out = in.setBigContentTitle(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$BigTextStyle;true;setSummaryText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.BigTextStyle out = null; + NotificationCompat.BigTextStyle in = (NotificationCompat.BigTextStyle) source(); + out = in.setSummaryText(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addAction;(Action);;Argument[0];Argument[-1];taint;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Action in = (NotificationCompat.Action) source(); + out.addAction(in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addAction;(int,CharSequence,PendingIntent);;Argument[2];Argument[-1];taint;manual" + NotificationCompat.Builder out = null; + PendingIntent in = (PendingIntent) source(); + out.addAction(0, null, in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addAction;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.addAction(0, null, null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addAction;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.addAction(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addExtras;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.addExtras(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addExtras;;;Argument[0].MapKey;Argument[-1].SyntheticField[android.content.Intent.extras].MapKey;value;manual" + NotificationCompat.Builder out = null; + Bundle in = (Bundle) newWithMapKeyDefault(source()); + out.addExtras(in); + sink(getMapKeyDefault(out.getExtras())); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addExtras;;;Argument[0].MapValue;Argument[-1].SyntheticField[android.content.Intent.extras].MapValue;value;manual" + NotificationCompat.Builder out = null; + Bundle in = (Bundle) newWithMapValueDefault(source()); + out.addExtras(in); + sink(getMapValueDefault(out.getExtras())); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;addPerson;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.addPerson(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;build;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue.Field[android.app.Notification.extras];value;manual" + Notification out = null; + NotificationCompat.Builder builder = null; + Bundle in = (Bundle) newWithMapValueDefault(source()); + builder.addExtras(in); + out = builder.build(); + sink(getMapValueDefault(out.extras)); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;build;;;Argument[-1];ReturnValue;taint;manual" + Notification out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.build(); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;extend;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.extend(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;getExtras;;;Argument[-1].SyntheticField[android.content.Intent.extras];ReturnValue;value;manual" + Bundle out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.getExtras(); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setAutoCancel;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setAutoCancel(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setBadgeIconType;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setBadgeIconType(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setBubbleMetadata;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setBubbleMetadata(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setCategory;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setCategory(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setChannelId;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setChannelId(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setChronometerCountDown;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setChronometerCountDown(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setColor;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setColor(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setColorized;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setColorized(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContent;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setContent(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContentInfo;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setContentInfo(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContentIntent;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setContentIntent(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContentIntent;;;Argument[0];Argument[-1];taint;manual" + NotificationCompat.Builder out = null; + PendingIntent in = (PendingIntent) source(); + out.setContentIntent(in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContentText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setContentText(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setContentTitle;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setContentTitle(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setCustomBigContentView;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setCustomBigContentView(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setCustomHeadsUpContentView;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setCustomHeadsUpContentView(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setDefaults;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setDefaults(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setDeleteIntent;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setDeleteIntent(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setDeleteIntent;;;Argument[0];Argument[-1];taint;manual" + NotificationCompat.Builder out = null; + PendingIntent in = (PendingIntent) source(); + out.setDeleteIntent(in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setExtras;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setExtras(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setExtras;;;Argument[0];Argument[-1].SyntheticField[android.content.Intent.extras];value;manual" + NotificationCompat.Builder out = null; + Bundle in = (Bundle) source(); + out.setExtras(in); + sink(out.getExtras()); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setFullScreenIntent;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setFullScreenIntent(null, false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setGroup;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setGroup(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setGroupAlertBehavior;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setGroupAlertBehavior(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setGroupSummary;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setGroupSummary(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setLargeIcon;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setLargeIcon(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setLights;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setLights(0, 0, 0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setLocalOnly;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setLocalOnly(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setNumber;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setNumber(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setOngoing;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setOngoing(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setOnlyAlertOnce;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setOnlyAlertOnce(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setPriority;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setPriority(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setProgress;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setProgress(0, 0, false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setPublicVersion;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setPublicVersion(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setPublicVersion;;;Argument[0];Argument[-1];taint;manual" + NotificationCompat.Builder out = null; + Notification in = (Notification) source(); + out.setPublicVersion(in); + sink(out); // $ hasTaintFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setRemoteInputHistory;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setRemoteInputHistory(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setShortcutId;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setShortcutId(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setShowWhen;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setShowWhen(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSmallIcon;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSmallIcon(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSmallIcon;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSmallIcon(0, 0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSortKey;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSortKey(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSound;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSound(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSound;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSound(null, 0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setStyle;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setStyle(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setSubText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setSubText(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setTicker;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setTicker(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setTicker;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setTicker(null, null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setTimeoutAfter;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setTimeoutAfter(0L); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setUsesChronometer;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setUsesChronometer(false); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setVibrate;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setVibrate(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setVisibility;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setVisibility(0); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$Builder;true;setWhen;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.Builder out = null; + NotificationCompat.Builder in = (NotificationCompat.Builder) source(); + out = in.setWhen(0L); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$InboxStyle;true;addLine;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.InboxStyle out = null; + NotificationCompat.InboxStyle in = (NotificationCompat.InboxStyle) source(); + out = in.addLine(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$InboxStyle;true;setBigContentTitle;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.InboxStyle out = null; + NotificationCompat.InboxStyle in = (NotificationCompat.InboxStyle) source(); + out = in.setBigContentTitle(null); + sink(out); // $ hasValueFlow + } + { + // "androidx.core.app;NotificationCompat$InboxStyle;true;setSummaryText;;;Argument[-1];ReturnValue;value;manual" + NotificationCompat.InboxStyle out = null; + NotificationCompat.InboxStyle in = (NotificationCompat.InboxStyle) source(); + out = in.setSummaryText(null); + sink(out); // $ hasValueFlow + } } } diff --git a/java/ql/test/library-tests/frameworks/play/PlayMvcHttpRequestHeader.expected b/java/ql/test/library-tests/frameworks/play/PlayMvcHttpRequestHeader.expected index 79ab2a9ded6..9ccaba383d1 100644 --- a/java/ql/test/library-tests/frameworks/play/PlayMvcHttpRequestHeader.expected +++ b/java/ql/test/library-tests/frameworks/play/PlayMvcHttpRequestHeader.expected @@ -1,27 +1,27 @@ -| play.mvc.Http$RequestHeader | RequestHeader.acceptLanguages | -| play.mvc.Http$RequestHeader | RequestHeader.accepts | -| play.mvc.Http$RequestHeader | RequestHeader.addAttr | -| play.mvc.Http$RequestHeader | RequestHeader.attrs | -| play.mvc.Http$RequestHeader | RequestHeader.charset | -| play.mvc.Http$RequestHeader | RequestHeader.clientCertificateChain | -| play.mvc.Http$RequestHeader | RequestHeader.contentType | -| play.mvc.Http$RequestHeader | RequestHeader.cookie | -| play.mvc.Http$RequestHeader | RequestHeader.cookies | -| play.mvc.Http$RequestHeader | RequestHeader.getHeader | -| play.mvc.Http$RequestHeader | RequestHeader.getHeaders | -| play.mvc.Http$RequestHeader | RequestHeader.getQueryString | -| play.mvc.Http$RequestHeader | RequestHeader.hasBody | -| play.mvc.Http$RequestHeader | RequestHeader.hasHeader | -| play.mvc.Http$RequestHeader | RequestHeader.header | -| play.mvc.Http$RequestHeader | RequestHeader.headers | -| play.mvc.Http$RequestHeader | RequestHeader.host | -| play.mvc.Http$RequestHeader | RequestHeader.method | -| play.mvc.Http$RequestHeader | RequestHeader.path | -| play.mvc.Http$RequestHeader | RequestHeader.queryString | -| play.mvc.Http$RequestHeader | RequestHeader.remoteAddress | -| play.mvc.Http$RequestHeader | RequestHeader.secure | -| play.mvc.Http$RequestHeader | RequestHeader.tags | -| play.mvc.Http$RequestHeader | RequestHeader.uri | -| play.mvc.Http$RequestHeader | RequestHeader.version | -| play.mvc.Http$RequestHeader | RequestHeader.withAttrs | -| play.mvc.Http$RequestHeader | RequestHeader.withBody | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.acceptLanguages | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.accepts | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.addAttr | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.attrs | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.charset | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.clientCertificateChain | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.contentType | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.cookie | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.cookies | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.getHeader | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.getHeaders | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.getQueryString | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.hasBody | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.hasHeader | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.header | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.headers | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.host | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.method | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.path | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.queryString | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.remoteAddress | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.secure | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.tags | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.uri | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.version | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.withAttrs | +| play.mvc.Http$RequestHeader | play.mvc.Http$RequestHeader.withBody | diff --git a/java/ql/test/library-tests/frameworks/play/PlayMvcResultsClass.expected b/java/ql/test/library-tests/frameworks/play/PlayMvcResultsClass.expected index 4553e207bf6..0659069aa13 100644 --- a/java/ql/test/library-tests/frameworks/play/PlayMvcResultsClass.expected +++ b/java/ql/test/library-tests/frameworks/play/PlayMvcResultsClass.expected @@ -1,19 +1,19 @@ -| play.mvc.Results | Results. | -| play.mvc.Results | Results.badRequest | -| play.mvc.Results | Results.created | -| play.mvc.Results | Results.forbidden | -| play.mvc.Results | Results.found | -| play.mvc.Results | Results.internalServerError | -| play.mvc.Results | Results.movedPermanently | -| play.mvc.Results | Results.noContent | -| play.mvc.Results | Results.notAcceptable | -| play.mvc.Results | Results.notFound | -| play.mvc.Results | Results.ok | -| play.mvc.Results | Results.paymentRequired | -| play.mvc.Results | Results.permanentRedirect | -| play.mvc.Results | Results.redirect | -| play.mvc.Results | Results.seeOther | -| play.mvc.Results | Results.status | -| play.mvc.Results | Results.temporaryRedirect | -| play.mvc.Results | Results.unauthorized | -| play.mvc.Results | Results.unsupportedMediaType | +| play.mvc.Results | play.mvc.Results. | +| play.mvc.Results | play.mvc.Results.badRequest | +| play.mvc.Results | play.mvc.Results.created | +| play.mvc.Results | play.mvc.Results.forbidden | +| play.mvc.Results | play.mvc.Results.found | +| play.mvc.Results | play.mvc.Results.internalServerError | +| play.mvc.Results | play.mvc.Results.movedPermanently | +| play.mvc.Results | play.mvc.Results.noContent | +| play.mvc.Results | play.mvc.Results.notAcceptable | +| play.mvc.Results | play.mvc.Results.notFound | +| play.mvc.Results | play.mvc.Results.ok | +| play.mvc.Results | play.mvc.Results.paymentRequired | +| play.mvc.Results | play.mvc.Results.permanentRedirect | +| play.mvc.Results | play.mvc.Results.redirect | +| play.mvc.Results | play.mvc.Results.seeOther | +| play.mvc.Results | play.mvc.Results.status | +| play.mvc.Results | play.mvc.Results.temporaryRedirect | +| play.mvc.Results | play.mvc.Results.unauthorized | +| play.mvc.Results | play.mvc.Results.unsupportedMediaType | diff --git a/java/ql/test/library-tests/implicit-this-type/Test.java b/java/ql/test/library-tests/implicit-this-type/Test.java new file mode 100644 index 00000000000..0eff32dddaf --- /dev/null +++ b/java/ql/test/library-tests/implicit-this-type/Test.java @@ -0,0 +1,83 @@ +class Gen { + void m(T t) { } +} + +class SubSpec extends Gen { + void foo() { + m("direct implicit this"); + this.m("direct explicit this"); + } + + class Inner { + void bar() { + m("direct implicit this from inner"); + SubSpec.this.m("direct explicit this from inner"); + } + } + + void hasLocal() { + class Local { + void baz() { + m("direct implicit this from local"); + SubSpec.this.m("direct explicit this from local"); + } + } + } +} + +class SubGen extends Gen { + void foo() { + m((S)"direct implicit this (generic sub)"); + this.m((S)"direct explicit this (generic sub)"); + } + + class Inner { + void bar() { + m((S)"direct implicit this from inner (generic sub)"); + SubGen.this.m((S)"direct explicit this from inner (generic sub)"); + } + } +} + +class Intermediate extends Gen { } + +class GrandchildSpec extends Intermediate { + void foo() { + m("indirect implicit this"); + this.m("indirect explicit this"); + } + + class Inner { + void bar() { + m("indirect implicit this from inner"); + GrandchildSpec.this.m("indirect explicit this from inner"); + } + } +} + +class GrandchildGen extends Intermediate { + void foo() { + m((R)"indirect implicit this (generic sub)"); + this.m((R)"indirect explicit this (generic sub)"); + } + + class Inner { + void bar() { + m((R)"indirect implicit this from inner (generic sub)"); + GrandchildGen.this.m((R)"indirect explicit this from inner (generic sub)"); + } + } +} + +class UninvolvedOuter { + class InnerGen { + void m(T t) { } + } + + class InnerSpec extends InnerGen { + void foo() { + m("direct implicit this, from inner to inner"); + this.m("direct explicit this, from inner to inner"); + } + } +} diff --git a/java/ql/test/library-tests/implicit-this-type/test.expected b/java/ql/test/library-tests/implicit-this-type/test.expected new file mode 100644 index 00000000000..3ce0796397b --- /dev/null +++ b/java/ql/test/library-tests/implicit-this-type/test.expected @@ -0,0 +1,20 @@ +| Test.java:7:5:7:29 | m(...) | Test.java:7:7:7:28 | "direct implicit this" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:8:5:8:34 | m(...) | Test.java:8:12:8:33 | "direct explicit this" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:13:7:13:42 | m(...) | Test.java:13:9:13:41 | "direct implicit this from inner" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:14:7:14:55 | m(...) | Test.java:14:22:14:54 | "direct explicit this from inner" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:21:9:21:44 | m(...) | Test.java:21:11:21:43 | "direct implicit this from local" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:22:9:22:57 | m(...) | Test.java:22:24:22:56 | "direct explicit this from local" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:30:5:30:46 | m(...) | Test.java:30:10:30:45 | "direct implicit this (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:31:5:31:51 | m(...) | Test.java:31:15:31:50 | "direct explicit this (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:36:7:36:59 | m(...) | Test.java:36:12:36:58 | "direct implicit this from inner (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:37:7:37:71 | m(...) | Test.java:37:24:37:70 | "direct explicit this from inner (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:46:5:46:31 | m(...) | Test.java:46:7:46:30 | "indirect implicit this" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:47:5:47:36 | m(...) | Test.java:47:12:47:35 | "indirect explicit this" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:52:7:52:44 | m(...) | Test.java:52:9:52:43 | "indirect implicit this from inner" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:53:7:53:64 | m(...) | Test.java:53:29:53:63 | "indirect explicit this from inner" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:60:5:60:48 | m(...) | Test.java:60:10:60:47 | "indirect implicit this (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:61:5:61:53 | m(...) | Test.java:61:15:61:52 | "indirect explicit this (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:66:7:66:61 | m(...) | Test.java:66:12:66:60 | "indirect implicit this from inner (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:67:7:67:80 | m(...) | Test.java:67:31:67:79 | "indirect explicit this from inner (generic sub)" | Gen.class:0:0:0:0 | m | Gen.class:0:0:0:0 | Gen | +| Test.java:79:7:79:52 | m(...) | Test.java:79:9:79:51 | "direct implicit this, from inner to inner" | UninvolvedOuter$InnerGen.class:0:0:0:0 | m | UninvolvedOuter$InnerGen.class:0:0:0:0 | InnerGen | +| Test.java:80:7:80:57 | m(...) | Test.java:80:14:80:56 | "direct explicit this, from inner to inner" | UninvolvedOuter$InnerGen.class:0:0:0:0 | m | UninvolvedOuter$InnerGen.class:0:0:0:0 | InnerGen | diff --git a/java/ql/test/library-tests/implicit-this-type/test.ql b/java/ql/test/library-tests/implicit-this-type/test.ql new file mode 100644 index 00000000000..f3b1774ff6b --- /dev/null +++ b/java/ql/test/library-tests/implicit-this-type/test.ql @@ -0,0 +1,5 @@ +import java + +from MethodAccess ma +select ma, ma.getAnArgument().getAChildExpr*().(StringLiteral), ma.getCallee(), + ma.getCallee().getDeclaringType() diff --git a/java/ql/test/library-tests/typeflow/UnionTypes.java b/java/ql/test/library-tests/typeflow/UnionTypes.java new file mode 100644 index 00000000000..a82b3828d2f --- /dev/null +++ b/java/ql/test/library-tests/typeflow/UnionTypes.java @@ -0,0 +1,54 @@ +import java.util.*; +import java.util.concurrent.*; + +public class UnionTypes { + public void m1() { + m1_map_put(new LinkedHashMap<>(), "k", "v"); + m1_map_put(new ConcurrentHashMap<>(), "k", "v"); + } + + private void m1_map_put(Map m, String k, String v) { + m.put(k, v); + } + + static class Sup { } + interface Inter { } + + static class A1 extends Sup implements Inter { } + static class A2 extends Sup implements Inter { } + static class A3 extends Sup { } + static class A4 extends Sup implements Inter { } + static class A2sub extends A2 { } + + private void m2(boolean b) { + scc1(new A1(), 10); + Sup x = b ? new A2() : new A3(); + scc2(x, 10); + } + + private void scc1(Sup x1, int i) { + scc2(x1, i); + } + + private void scc2(Sup x2, int i) { + scc3(x2, i); + } + + private void scc3(Sup x3, int i) { + next(x3); + if (i > 0) + scc1(x3, --i); + } + + private void next(Sup x) { + if (x instanceof Inter) { + x.hashCode(); + } + } + + void m3(Object d) { + if (d instanceof A1 || d instanceof A2 || d instanceof A3) { + d.hashCode(); + } + } +} diff --git a/java/ql/test/library-tests/typeflow/typeflow.expected b/java/ql/test/library-tests/typeflow/typeflow.expected index 1f879fe37ea..021d04b55d3 100644 --- a/java/ql/test/library-tests/typeflow/typeflow.expected +++ b/java/ql/test/library-tests/typeflow/typeflow.expected @@ -13,3 +13,4 @@ | A.java:67:22:67:22 | x | Integer | false | | A.java:70:23:70:24 | x2 | Integer | false | | A.java:92:18:92:18 | n | Integer | false | +| UnionTypes.java:45:7:45:7 | x | Inter | false | diff --git a/java/ql/test/library-tests/typeflow/uniontypeflow.expected b/java/ql/test/library-tests/typeflow/uniontypeflow.expected new file mode 100644 index 00000000000..7c595175301 --- /dev/null +++ b/java/ql/test/library-tests/typeflow/uniontypeflow.expected @@ -0,0 +1,24 @@ +| UnionTypes.java:11:5:11:5 | m | 2 | ConcurrentHashMap | false | +| UnionTypes.java:11:5:11:5 | m | 2 | LinkedHashMap | false | +| UnionTypes.java:26:10:26:10 | x | 2 | A2 | true | +| UnionTypes.java:26:10:26:10 | x | 2 | A3 | false | +| UnionTypes.java:30:10:30:11 | x1 | 3 | A1 | false | +| UnionTypes.java:30:10:30:11 | x1 | 3 | A2 | true | +| UnionTypes.java:30:10:30:11 | x1 | 3 | A3 | false | +| UnionTypes.java:34:10:34:11 | x2 | 3 | A1 | false | +| UnionTypes.java:34:10:34:11 | x2 | 3 | A2 | true | +| UnionTypes.java:34:10:34:11 | x2 | 3 | A3 | false | +| UnionTypes.java:38:10:38:11 | x3 | 3 | A1 | false | +| UnionTypes.java:38:10:38:11 | x3 | 3 | A2 | true | +| UnionTypes.java:38:10:38:11 | x3 | 3 | A3 | false | +| UnionTypes.java:40:12:40:13 | x3 | 3 | A1 | false | +| UnionTypes.java:40:12:40:13 | x3 | 3 | A2 | true | +| UnionTypes.java:40:12:40:13 | x3 | 3 | A3 | false | +| UnionTypes.java:44:9:44:9 | x | 3 | A1 | false | +| UnionTypes.java:44:9:44:9 | x | 3 | A2 | true | +| UnionTypes.java:44:9:44:9 | x | 3 | A3 | false | +| UnionTypes.java:45:7:45:7 | x | 2 | A1 | false | +| UnionTypes.java:45:7:45:7 | x | 2 | A2 | true | +| UnionTypes.java:51:7:51:7 | d | 3 | A1 | false | +| UnionTypes.java:51:7:51:7 | d | 3 | A2 | false | +| UnionTypes.java:51:7:51:7 | d | 3 | A3 | false | diff --git a/java/ql/test/library-tests/typeflow/uniontypeflow.ql b/java/ql/test/library-tests/typeflow/uniontypeflow.ql new file mode 100644 index 00000000000..769ac89172c --- /dev/null +++ b/java/ql/test/library-tests/typeflow/uniontypeflow.ql @@ -0,0 +1,10 @@ +import java +import semmle.code.java.dataflow.TypeFlow + +int countUnionTypes(Expr e) { + result = strictcount(RefType t, boolean exact | exprUnionTypeFlow(e, t, exact)) +} + +from RValue e, RefType t, boolean exact +where exprUnionTypeFlow(e, t, exact) +select e, countUnionTypes(e), t.toString(), exact diff --git a/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.expected b/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.expected new file mode 100644 index 00000000000..e8c6ba5752a --- /dev/null +++ b/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.expected @@ -0,0 +1,2 @@ +| GenericTest.java:14:7:14:9 | f(...) | A $@ is called instead of a $@. | GenericTest.class:0:0:0:0 | f | method declared in a superclass | GenericTest.java:9:8:9:8 | f | method with the same signature in an enclosing class | +| Test.java:14:7:14:9 | f(...) | A $@ is called instead of a $@. | Test.java:3:8:3:8 | f | method declared in a superclass | Test.java:9:8:9:8 | f | method with the same signature in an enclosing class | diff --git a/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.qlref b/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.qlref new file mode 100644 index 00000000000..70c62b8c851 --- /dev/null +++ b/java/ql/test/query-tests/AmbiguousOuterSuper/AmbiguousOuterSuper.qlref @@ -0,0 +1 @@ +Violations of Best Practice/Naming Conventions/AmbiguousOuterSuper.ql \ No newline at end of file diff --git a/java/ql/test/query-tests/AmbiguousOuterSuper/GenericTest.java b/java/ql/test/query-tests/AmbiguousOuterSuper/GenericTest.java new file mode 100644 index 00000000000..f0d14dc4867 --- /dev/null +++ b/java/ql/test/query-tests/AmbiguousOuterSuper/GenericTest.java @@ -0,0 +1,19 @@ +public class GenericTest { + + void f() { } + +} + +class Outer2 { + + void f() { } + + class Inner extends GenericTest { + + public void test() { + f(); + } + + } + +} diff --git a/java/ql/test/query-tests/AmbiguousOuterSuper/Test.java b/java/ql/test/query-tests/AmbiguousOuterSuper/Test.java new file mode 100644 index 00000000000..e2a506f1438 --- /dev/null +++ b/java/ql/test/query-tests/AmbiguousOuterSuper/Test.java @@ -0,0 +1,19 @@ +public class Test { + + void f() { } + +} + +class Outer { + + void f() { } + + class Inner extends Test { + + public void test() { + f(); + } + + } + +} diff --git a/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected new file mode 100644 index 00000000000..0056c25bb53 --- /dev/null +++ b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected @@ -0,0 +1,7 @@ +| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:5:5:5:19 | User.java:5:5:5:19 | after this call to getStrings | +| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:13:12:13:26 | User.java:13:12:13:26 | after this call to getStrings | +| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:38:12:38:26 | User.java:38:12:38:26 | after this call to getStrings | +| ExposesRep.java:13:30:13:41 | getStringMap | getStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:9:5:9:21 | User.java:9:5:9:21 | after this call to getStringMap | +| ExposesRep.java:17:15:17:24 | setStrings | setStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:22:5:22:6 | User.java:22:5:22:6 | through the variable ss | +| ExposesRep.java:21:15:21:26 | setStringMap | setStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:27:5:27:5 | User.java:27:5:27:5 | through the variable m | +| ExposesRep.java:29:14:29:21 | getArray | getArray exposes the internal representation stored in field array. The value may be modified $@. | User.java:31:5:31:18 | User.java:31:5:31:18 | after this call to getArray | diff --git a/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.qlref b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.qlref new file mode 100644 index 00000000000..6452bb942d2 --- /dev/null +++ b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.qlref @@ -0,0 +1 @@ +Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql diff --git a/java/ql/test/query-tests/ExposeRepresentation/ExposesRep.java b/java/ql/test/query-tests/ExposeRepresentation/ExposesRep.java new file mode 100644 index 00000000000..11cf4456788 --- /dev/null +++ b/java/ql/test/query-tests/ExposeRepresentation/ExposesRep.java @@ -0,0 +1,30 @@ +import java.util.Map; + +public class ExposesRep { + private String[] strings; + private Map stringMap; + + public ExposesRep() { + strings = new String[1]; + } + + public String[] getStrings() { return strings; } + + public Map getStringMap() { + return stringMap; + } + + public void setStrings(String[] ss) { + this.strings = ss; + } + + public void setStringMap(Map m) { + this.stringMap = m; + } +} + +class GenericExposesRep { + private T[] array; + + public T[] getArray() { return array; } +} diff --git a/java/ql/test/query-tests/ExposeRepresentation/User.java b/java/ql/test/query-tests/ExposeRepresentation/User.java new file mode 100644 index 00000000000..e13580cfa32 --- /dev/null +++ b/java/ql/test/query-tests/ExposeRepresentation/User.java @@ -0,0 +1,45 @@ +import java.util.Map; + +public class User { + public static void test1(ExposesRep er) { + er.getStrings()[0] = "Hello world"; + } + + public static void test2(ExposesRep er) { + er.getStringMap().put("Hello", "world"); + } + + public String[] indirectGetStrings(ExposesRep er) { + return er.getStrings(); + } + + public void test3(ExposesRep er) { + indirectGetStrings(er)[0] = "Hello world"; + } + + public static void test4(ExposesRep er, String[] ss) { + er.setStrings(ss); + ss[0] = "Hello world"; + } + + public static void test5(ExposesRep er, Map m) { + er.setStringMap(m); + m.put("Hello", "world"); + } + + public static void test6(GenericExposesRep ger) { + ger.getArray()[0] = "Hello world"; + } +} + +class GenericUser { + + public String[] indirectGetStrings(ExposesRep er) { + return er.getStrings(); + } + + public static void test1(ExposesRep er, GenericUser gu) { + gu.indirectGetStrings(er)[0] = "Hello world"; + } + +} diff --git a/java/ql/test/query-tests/IteratorRemoveMayFail/IteratorRemoveMayFail.expected b/java/ql/test/query-tests/IteratorRemoveMayFail/IteratorRemoveMayFail.expected index 8509ce7dde7..85653b45351 100644 --- a/java/ql/test/query-tests/IteratorRemoveMayFail/IteratorRemoveMayFail.expected +++ b/java/ql/test/query-tests/IteratorRemoveMayFail/IteratorRemoveMayFail.expected @@ -1,2 +1,3 @@ | Test.java:16:5:16:17 | remove(...) | This call may fail when iterating over the collection created $@, since it does not support element removal. | Test.java:29:16:29:32 | asList(...) | here | | Test.java:16:5:16:17 | remove(...) | This call may fail when iterating over the collection created $@, since it does not support element removal. | Test.java:33:16:33:43 | singletonList(...) | here | +| Test.java:44:3:44:23 | remove(...) | This call may fail when iterating over the collection created $@, since it does not support element removal. | Test.java:52:15:52:31 | asList(...) | here | diff --git a/java/ql/test/query-tests/IteratorRemoveMayFail/Test.java b/java/ql/test/query-tests/IteratorRemoveMayFail/Test.java index 782c1d3b565..3ed2c563327 100644 --- a/java/ql/test/query-tests/IteratorRemoveMayFail/Test.java +++ b/java/ql/test/query-tests/IteratorRemoveMayFail/Test.java @@ -36,4 +36,20 @@ class A { public List getL() { return l; } +} + +class Parent { + + public void removeFirst(List l) { + l.iterator().remove(); + } + +} + +class Child extends Parent { + + public void test(String... ss) { + removeFirst(Arrays.asList(ss)); + } + } \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected index 830f4d76085..1d2935fe0d4 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/TaintedPath.expected @@ -9,6 +9,11 @@ edges | Test.java:80:31:80:32 | br : BufferedReader | Test.java:80:31:80:43 | readLine(...) : String | | Test.java:80:31:80:43 | readLine(...) : String | Test.java:82:67:82:81 | ... + ... | | Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | +| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:97:12:97:33 | new URI(...) | +| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:98:12:98:33 | new URI(...) | +| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:99:12:99:33 | new URI(...) | +| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:100:12:100:45 | new URI(...) | +| Test.java:95:14:95:34 | getHostName(...) : String | Test.java:101:12:101:54 | new URI(...) | nodes | Test.java:19:18:19:38 | getHostName(...) : String | semmle.label | getHostName(...) : String | | Test.java:24:20:24:23 | temp | semmle.label | temp | @@ -23,6 +28,12 @@ nodes | Test.java:82:67:82:81 | ... + ... | semmle.label | ... + ... | | Test.java:88:17:88:37 | getHostName(...) : String | semmle.label | getHostName(...) : String | | Test.java:90:26:90:29 | temp | semmle.label | temp | +| Test.java:95:14:95:34 | getHostName(...) : String | semmle.label | getHostName(...) : String | +| Test.java:97:12:97:33 | new URI(...) | semmle.label | new URI(...) | +| Test.java:98:12:98:33 | new URI(...) | semmle.label | new URI(...) | +| Test.java:99:12:99:33 | new URI(...) | semmle.label | new URI(...) | +| Test.java:100:12:100:45 | new URI(...) | semmle.label | new URI(...) | +| Test.java:101:12:101:54 | new URI(...) | semmle.label | new URI(...) | subpaths #select | Test.java:24:11:24:24 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:24:20:24:23 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | @@ -31,3 +42,8 @@ subpaths | Test.java:34:12:34:25 | new File(...) | Test.java:19:18:19:38 | getHostName(...) : String | Test.java:34:21:34:24 | temp | $@ flows to here and is used in a path. | Test.java:19:18:19:38 | getHostName(...) | User-provided value | | Test.java:82:52:82:88 | new FileWriter(...) | Test.java:79:74:79:97 | getInputStream(...) : ServletInputStream | Test.java:82:67:82:81 | ... + ... | $@ flows to here and is used in a path. | Test.java:79:74:79:97 | getInputStream(...) | User-provided value | | Test.java:90:26:90:29 | temp | Test.java:88:17:88:37 | getHostName(...) : String | Test.java:90:26:90:29 | temp | $@ flows to here and is used in a path. | Test.java:88:17:88:37 | getHostName(...) | User-provided value | +| Test.java:97:3:97:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:97:12:97:33 | new URI(...) | $@ flows to here and is used in a path. | Test.java:95:14:95:34 | getHostName(...) | User-provided value | +| Test.java:98:3:98:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:98:12:98:33 | new URI(...) | $@ flows to here and is used in a path. | Test.java:95:14:95:34 | getHostName(...) | User-provided value | +| Test.java:99:3:99:34 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:99:12:99:33 | new URI(...) | $@ flows to here and is used in a path. | Test.java:95:14:95:34 | getHostName(...) | User-provided value | +| Test.java:100:3:100:46 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:100:12:100:45 | new URI(...) | $@ flows to here and is used in a path. | Test.java:95:14:95:34 | getHostName(...) | User-provided value | +| Test.java:101:3:101:55 | new File(...) | Test.java:95:14:95:34 | getHostName(...) : String | Test.java:101:12:101:54 | new URI(...) | $@ flows to here and is used in a path. | Test.java:95:14:95:34 | getHostName(...) | User-provided value | diff --git a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java index f0d0147df08..46c3eeec001 100644 --- a/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java +++ b/java/ql/test/query-tests/security/CWE-022/semmle/tests/Test.java @@ -6,7 +6,7 @@ import javax.servlet.http.*; import javax.servlet.ServletException; import java.io.*; -import java.net.InetAddress; +import java.net.*; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.FileSystems; @@ -89,4 +89,15 @@ class Test { // BAD: open a file based on user input, using a MaD-documented API new LockableFileWriter(temp); } + + void doGet5(InetAddress address) + throws URISyntaxException { + String t = address.getHostName(); + // BAD: construct a file path with user input + new File(new URI(null, t, null)); + new File(new URI(t, t, null, t)); + new File(new URI(t, null, t, t)); + new File(new URI(null, null, t, null, null)); + new File(new URI(null, null, null, 0, t, null, null)); + } } diff --git a/java/ql/test/query-tests/security/CWE-312/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/AndroidManifest.xml similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/AndroidManifest.xml rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/AndroidManifest.xml diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.expected b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.expected similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.expected rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.expected diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.java b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.java similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.java rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.java diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.ql b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.ql similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidDatabaseTest.ql rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidDatabaseTest.ql diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.expected b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.expected similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.expected rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.expected diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.java b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.java similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.java rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.java diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.ql b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.ql similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageAndroidFilesystemTest.ql rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageAndroidFilesystemTest.ql diff --git a/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.expected b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.expected new file mode 100644 index 00000000000..e69de29bb2d diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.java b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.java similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.java rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.java diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.ql b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.ql similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTest.ql rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTest.ql diff --git a/java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTestKt.kt similarity index 100% rename from java/ql/test/query-tests/security/CWE-312/CleartextStorageSharedPrefsTestKt.kt rename to java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/CleartextStorageSharedPrefsTestKt.kt diff --git a/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/options b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/options new file mode 100644 index 00000000000..250480dbe1a --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/CleartextStorage/options @@ -0,0 +1,2 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/google-android-9.0.0 +// codeql-extractor-kotlin-options: ${testdir}/../../../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.expected b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.expected new file mode 100644 index 00000000000..a4e41d774ca --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.expected @@ -0,0 +1,2 @@ +| TestExplicitlyEnabled/AndroidManifest.xml:6:5:27:19 | application | Backups are allowed in this Android application. | +| TestMissing/AndroidManifest.xml:6:5:27:19 | application | Backups are allowed in this Android application. | diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.java b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.java new file mode 100644 index 00000000000..f409a442f06 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.java @@ -0,0 +1,3 @@ +class AllowBackupEnabledTest { + +} diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.qlref b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.qlref new file mode 100644 index 00000000000..2b7a5375dab --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/AllowBackupEnabledTest.qlref @@ -0,0 +1 @@ +Security/CWE/CWE-312/AllowBackupAttributeEnabled.ql \ No newline at end of file diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/TestEmptyManifest/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/TestEmptyManifest/AndroidManifest.xml new file mode 100644 index 00000000000..306f3852be8 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/TestEmptyManifest/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyDisabled/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyDisabled/AndroidManifest.xml new file mode 100644 index 00000000000..5f35eb615d5 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyDisabled/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyEnabled/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyEnabled/AndroidManifest.xml new file mode 100644 index 00000000000..4b69c52ccae --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/TestExplicitlyEnabled/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/TestLibrary/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/TestLibrary/AndroidManifest.xml new file mode 100644 index 00000000000..e83d18d59e9 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/TestLibrary/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/TestMissing/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/TestMissing/AndroidManifest.xml new file mode 100644 index 00000000000..9db4c7429fe --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/TestMissing/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/Testbuild/AndroidManifest.xml b/java/ql/test/query-tests/security/CWE-312/android/backup/Testbuild/AndroidManifest.xml new file mode 100644 index 00000000000..097e7ed2b51 --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/Testbuild/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + diff --git a/java/ql/test/query-tests/security/CWE-312/android/backup/options b/java/ql/test/query-tests/security/CWE-312/android/backup/options new file mode 100644 index 00000000000..250480dbe1a --- /dev/null +++ b/java/ql/test/query-tests/security/CWE-312/android/backup/options @@ -0,0 +1,2 @@ +// semmle-extractor-options: --javac-args -cp ${testdir}/../../../../../stubs/google-android-9.0.0 +// codeql-extractor-kotlin-options: ${testdir}/../../../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/query-tests/security/CWE-312/options b/java/ql/test/query-tests/security/CWE-312/options deleted file mode 100644 index 93845c4b2a8..00000000000 --- a/java/ql/test/query-tests/security/CWE-312/options +++ /dev/null @@ -1,2 +0,0 @@ -// semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/google-android-9.0.0 -// codeql-extractor-kotlin-options: ${testdir}/../../../stubs/google-android-9.0.0 diff --git a/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java b/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java index 3896a20799e..9c8f098d467 100644 --- a/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java +++ b/java/ql/test/query-tests/security/CWE-927/ImplicitPendingIntentsTest.java @@ -14,6 +14,8 @@ import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; import android.os.RemoteException; +import androidx.core.app.AlarmManagerCompat; +import androidx.core.app.NotificationManagerCompat; import androidx.core.graphics.drawable.IconCompat; import androidx.slice.Slice; import androidx.slice.SliceProvider; @@ -182,7 +184,7 @@ public class ImplicitPendingIntentsTest { Notification.Builder nBuilder = new Notification.Builder(ctx).addAction(aBuilder.build()); Notification notification = nBuilder.build(); - NotificationManager nManager = new NotificationManager(); + NotificationManager nManager = null; nManager.notifyAsPackage("targetPackage", "tag", 0, notification); // $hasImplicitPendingIntent nManager.notify(0, notification); // $hasImplicitPendingIntent nManager.notifyAsUser("", 0, notification, null); // $hasImplicitPendingIntent @@ -195,7 +197,7 @@ public class ImplicitPendingIntentsTest { Notification.Builder nBuilder = new Notification.Builder(ctx).addAction(aBuilder.build()); Notification notification = nBuilder.build(); - NotificationManager nManager = new NotificationManager(); + NotificationManager nManager = null; nManager.notify(0, notification); // Safe } { @@ -212,10 +214,21 @@ public class ImplicitPendingIntentsTest { Notification.Action action = new Notification.Action(0, "", pi2); Notification.Builder nBuilder = new Notification.Builder(ctx).addAction(action); Notification notification = nBuilder.build(); - NotificationManager noMan = new NotificationManager(); + NotificationManager noMan = null; noMan.notify(0, notification); // Safe } - + // Compat sinks + { + Intent baseIntent = new Intent(); + PendingIntent pi = PendingIntent.getActivity(ctx, 0, baseIntent, 0); + Notification.Action.Builder aBuilder = new Notification.Action.Builder(0, "", pi); + Notification.Builder nBuilder = + new Notification.Builder(ctx).addAction(aBuilder.build()); + Notification notification = nBuilder.build(); + NotificationManagerCompat nManager = null; + nManager.notify(0, notification); // $hasImplicitPendingIntent + nManager.notify("", 0, notification); // $hasImplicitPendingIntent + } } public static void testPendingIntentInAnAlarm(Context ctx) { @@ -238,6 +251,16 @@ public class ImplicitPendingIntentsTest { PendingIntent.getActivity(ctx, 0, baseIntent, PendingIntent.FLAG_IMMUTABLE); // Sanitizer aManager.set(0, 0, pi); // Safe } + // Compat sinks + { + Intent baseIntent = new Intent(); + PendingIntent pi = PendingIntent.getActivity(ctx, 0, baseIntent, 0); + AlarmManagerCompat.setAlarmClock(aManager, 0, pi, null); // $hasImplicitPendingIntent + AlarmManagerCompat.setAlarmClock(aManager, 0, null, pi); // $hasImplicitPendingIntent + AlarmManagerCompat.setAndAllowWhileIdle(aManager, 0, 0, pi); // $hasImplicitPendingIntent + AlarmManagerCompat.setExact(aManager, 0, 0, pi); // $hasImplicitPendingIntent + AlarmManagerCompat.setExactAndAllowWhileIdle(aManager, 0, 0, pi); // $hasImplicitPendingIntent + } } static class TestActivity extends Activity { diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/AppComponentFactory.java b/java/ql/test/stubs/google-android-9.0.0/android/app/AppComponentFactory.java new file mode 100644 index 00000000000..284510fc655 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/AppComponentFactory.java @@ -0,0 +1,22 @@ +// Generated automatically from android.app.AppComponentFactory for testing purposes + +package android.app; + +import android.app.Activity; +import android.app.Application; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ContentProvider; +import android.content.Intent; +import android.content.pm.ApplicationInfo; + +public class AppComponentFactory +{ + public Activity instantiateActivity(ClassLoader p0, String p1, Intent p2){ return null; } + public AppComponentFactory(){} + public Application instantiateApplication(ClassLoader p0, String p1){ return null; } + public BroadcastReceiver instantiateReceiver(ClassLoader p0, String p1, Intent p2){ return null; } + public ClassLoader instantiateClassLoader(ClassLoader p0, ApplicationInfo p1){ return null; } + public ContentProvider instantiateProvider(ClassLoader p0, String p1){ return null; } + public Service instantiateService(ClassLoader p0, String p1, Intent p2){ return null; } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/app/AutomaticZenRule.java b/java/ql/test/stubs/google-android-9.0.0/android/app/AutomaticZenRule.java new file mode 100644 index 00000000000..3b97a1eca2d --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/app/AutomaticZenRule.java @@ -0,0 +1,37 @@ +// Generated automatically from android.app.AutomaticZenRule for testing purposes + +package android.app; + +import android.content.ComponentName; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; +import android.service.notification.ZenPolicy; + +public class AutomaticZenRule implements Parcelable +{ + protected AutomaticZenRule() {} + public AutomaticZenRule(Parcel p0){} + public AutomaticZenRule(String p0, ComponentName p1, ComponentName p2, Uri p3, ZenPolicy p4, int p5, boolean p6){} + public AutomaticZenRule(String p0, ComponentName p1, Uri p2, int p3, boolean p4){} + public ComponentName getConfigurationActivity(){ return null; } + public ComponentName getOwner(){ return null; } + public String getName(){ return null; } + public String toString(){ return null; } + public Uri getConditionId(){ return null; } + public ZenPolicy getZenPolicy(){ return null; } + public boolean equals(Object p0){ return false; } + public boolean isEnabled(){ return false; } + public int describeContents(){ return 0; } + public int getInterruptionFilter(){ return 0; } + public int hashCode(){ return 0; } + public long getCreationTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setConditionId(Uri p0){} + public void setConfigurationActivity(ComponentName p0){} + public void setEnabled(boolean p0){} + public void setInterruptionFilter(int p0){} + public void setName(String p0){} + public void setZenPolicy(ZenPolicy p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/service/notification/Condition.java b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/Condition.java new file mode 100644 index 00000000000..a7f94f23c42 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/Condition.java @@ -0,0 +1,41 @@ +// Generated automatically from android.service.notification.Condition for testing purposes + +package android.service.notification; + +import android.content.Context; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +public class Condition implements Parcelable +{ + protected Condition() {} + public Condition copy(){ return null; } + public Condition(Parcel p0){} + public Condition(Uri p0, String p1, String p2, String p3, int p4, int p5, int p6){} + public Condition(Uri p0, String p1, int p2){} + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public final String line1 = null; + public final String line2 = null; + public final String summary = null; + public final Uri id = null; + public final int flags = 0; + public final int icon = 0; + public final int state = 0; + public int describeContents(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static String SCHEME = null; + public static String relevanceToString(int p0){ return null; } + public static String stateToString(int p0){ return null; } + public static Uri.Builder newId(Context p0){ return null; } + public static boolean isValidId(Uri p0, String p1){ return false; } + public static int FLAG_RELEVANT_ALWAYS = 0; + public static int FLAG_RELEVANT_NOW = 0; + public static int STATE_ERROR = 0; + public static int STATE_FALSE = 0; + public static int STATE_TRUE = 0; + public static int STATE_UNKNOWN = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/service/notification/StatusBarNotification.java b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/StatusBarNotification.java new file mode 100644 index 00000000000..e360a5ad3f1 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/StatusBarNotification.java @@ -0,0 +1,37 @@ +// Generated automatically from android.service.notification.StatusBarNotification for testing purposes + +package android.service.notification; + +import android.app.Notification; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.UserHandle; + +public class StatusBarNotification implements Parcelable +{ + protected StatusBarNotification() {} + public Notification getNotification(){ return null; } + public StatusBarNotification clone(){ return null; } + public StatusBarNotification(Parcel p0){} + public StatusBarNotification(String p0, String p1, int p2, String p3, int p4, int p5, int p6, Notification p7, UserHandle p8, long p9){} + public String getGroupKey(){ return null; } + public String getKey(){ return null; } + public String getOpPkg(){ return null; } + public String getOverrideGroupKey(){ return null; } + public String getPackageName(){ return null; } + public String getTag(){ return null; } + public String toString(){ return null; } + public UserHandle getUser(){ return null; } + public boolean isAppGroup(){ return false; } + public boolean isClearable(){ return false; } + public boolean isGroup(){ return false; } + public boolean isOngoing(){ return false; } + public int describeContents(){ return 0; } + public int getId(){ return 0; } + public int getUid(){ return 0; } + public int getUserId(){ return 0; } + public long getPostTime(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public void setOverrideGroupKey(String p0){} + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/android/service/notification/ZenPolicy.java b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/ZenPolicy.java new file mode 100644 index 00000000000..81c52b0c18a --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/android/service/notification/ZenPolicy.java @@ -0,0 +1,47 @@ +// Generated automatically from android.service.notification.ZenPolicy for testing purposes + +package android.service.notification; + +import android.os.Parcel; +import android.os.Parcelable; + +public class ZenPolicy implements Parcelable +{ + public String toString(){ return null; } + public boolean equals(Object p0){ return false; } + public int describeContents(){ return 0; } + public int getPriorityCallSenders(){ return 0; } + public int getPriorityCategoryAlarms(){ return 0; } + public int getPriorityCategoryCalls(){ return 0; } + public int getPriorityCategoryConversations(){ return 0; } + public int getPriorityCategoryEvents(){ return 0; } + public int getPriorityCategoryMedia(){ return 0; } + public int getPriorityCategoryMessages(){ return 0; } + public int getPriorityCategoryReminders(){ return 0; } + public int getPriorityCategoryRepeatCallers(){ return 0; } + public int getPriorityCategorySystem(){ return 0; } + public int getPriorityConversationSenders(){ return 0; } + public int getPriorityMessageSenders(){ return 0; } + public int getVisualEffectAmbient(){ return 0; } + public int getVisualEffectBadge(){ return 0; } + public int getVisualEffectFullScreenIntent(){ return 0; } + public int getVisualEffectLights(){ return 0; } + public int getVisualEffectNotificationList(){ return 0; } + public int getVisualEffectPeek(){ return 0; } + public int getVisualEffectStatusBar(){ return 0; } + public int hashCode(){ return 0; } + public static Parcelable.Creator CREATOR = null; + public static int CONVERSATION_SENDERS_ANYONE = 0; + public static int CONVERSATION_SENDERS_IMPORTANT = 0; + public static int CONVERSATION_SENDERS_NONE = 0; + public static int CONVERSATION_SENDERS_UNSET = 0; + public static int PEOPLE_TYPE_ANYONE = 0; + public static int PEOPLE_TYPE_CONTACTS = 0; + public static int PEOPLE_TYPE_NONE = 0; + public static int PEOPLE_TYPE_STARRED = 0; + public static int PEOPLE_TYPE_UNSET = 0; + public static int STATE_ALLOW = 0; + public static int STATE_DISALLOW = 0; + public static int STATE_UNSET = 0; + public void writeToParcel(Parcel p0, int p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/AlarmManagerCompat.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/AlarmManagerCompat.java new file mode 100644 index 00000000000..adaf14a156b --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/AlarmManagerCompat.java @@ -0,0 +1,15 @@ +// Generated automatically from androidx.core.app.AlarmManagerCompat for testing purposes + +package androidx.core.app; + +import android.app.AlarmManager; +import android.app.PendingIntent; + +public class AlarmManagerCompat +{ + protected AlarmManagerCompat() {} + public static void setAlarmClock(AlarmManager p0, long p1, PendingIntent p2, PendingIntent p3){} + public static void setAndAllowWhileIdle(AlarmManager p0, int p1, long p2, PendingIntent p3){} + public static void setExact(AlarmManager p0, int p1, long p2, PendingIntent p3){} + public static void setExactAndAllowWhileIdle(AlarmManager p0, int p1, long p2, PendingIntent p3){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/CoreComponentFactory.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/CoreComponentFactory.java new file mode 100644 index 00000000000..699156f3441 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/CoreComponentFactory.java @@ -0,0 +1,25 @@ +// Generated automatically from androidx.core.app.CoreComponentFactory for testing purposes + +package androidx.core.app; + +import android.app.Activity; +import android.app.AppComponentFactory; +import android.app.Application; +import android.app.Service; +import android.content.BroadcastReceiver; +import android.content.ContentProvider; +import android.content.Intent; + +public class CoreComponentFactory extends AppComponentFactory +{ + public Activity instantiateActivity(ClassLoader p0, String p1, Intent p2){ return null; } + public Application instantiateApplication(ClassLoader p0, String p1){ return null; } + public BroadcastReceiver instantiateReceiver(ClassLoader p0, String p1, Intent p2){ return null; } + public ContentProvider instantiateProvider(ClassLoader p0, String p1){ return null; } + public CoreComponentFactory(){} + public Service instantiateService(ClassLoader p0, String p1, Intent p2){ return null; } + static public interface CompatWrapped + { + Object getWrapper(); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationBuilderWithBuilderAccessor.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationBuilderWithBuilderAccessor.java new file mode 100644 index 00000000000..028807b3000 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationBuilderWithBuilderAccessor.java @@ -0,0 +1,10 @@ +// Generated automatically from androidx.core.app.NotificationBuilderWithBuilderAccessor for testing +// purposes + +package androidx.core.app; + +import android.app.Notification; + +public interface NotificationBuilderWithBuilderAccessor { + Notification.Builder getBuilder(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationCompat.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationCompat.java new file mode 100644 index 00000000000..58beaab5b9c --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationCompat.java @@ -0,0 +1,720 @@ +// Generated automatically from androidx.core.app.NotificationCompat for testing purposes + +package androidx.core.app; + +import android.app.Notification; +import android.app.PendingIntent; +import android.content.Context; +import android.graphics.Bitmap; +import android.net.Uri; +import android.os.Bundle; +import android.widget.RemoteViews; +import androidx.core.app.NotificationBuilderWithBuilderAccessor; +import androidx.core.app.RemoteInput; +import androidx.core.graphics.drawable.IconCompat; +import java.util.ArrayList; +import java.util.List; + +public class NotificationCompat { + abstract static public class Style { + protected NotificationCompat.Builder mBuilder = null; + + protected void restoreFromCompatExtras(Bundle p0) {} + + public Bitmap createColoredBitmap(int p0, int p1) { + return null; + } + + public Notification build() { + return null; + } + + public RemoteViews applyStandardTemplate(boolean p0, int p1, boolean p2) { + return null; + } + + public RemoteViews makeBigContentView(NotificationBuilderWithBuilderAccessor p0) { + return null; + } + + public RemoteViews makeContentView(NotificationBuilderWithBuilderAccessor p0) { + return null; + } + + public RemoteViews makeHeadsUpContentView(NotificationBuilderWithBuilderAccessor p0) { + return null; + } + + public Style() {} + + public void addCompatExtras(Bundle p0) {} + + public void apply(NotificationBuilderWithBuilderAccessor p0) {} + + public void buildIntoRemoteViews(RemoteViews p0, RemoteViews p1) {} + + public void setBuilder(NotificationCompat.Builder p0) {} + } + + public NotificationCompat() {} + + public static Bundle getExtras(Notification p0) { + return null; + } + + public static CharSequence getContentTitle(Notification p0) { + return null; + } + + public static List getInvisibleActions(Notification p0) { + return null; + } + + public static NotificationCompat.Action getAction(Notification p0, int p1) { + return null; + } + + public static NotificationCompat.BubbleMetadata getBubbleMetadata(Notification p0) { + return null; + } + + public static String CATEGORY_ALARM = null; + public static String CATEGORY_CALL = null; + public static String CATEGORY_EMAIL = null; + public static String CATEGORY_ERROR = null; + public static String CATEGORY_EVENT = null; + public static String CATEGORY_MESSAGE = null; + public static String CATEGORY_NAVIGATION = null; + public static String CATEGORY_PROGRESS = null; + public static String CATEGORY_PROMO = null; + public static String CATEGORY_RECOMMENDATION = null; + public static String CATEGORY_REMINDER = null; + public static String CATEGORY_SERVICE = null; + public static String CATEGORY_SOCIAL = null; + public static String CATEGORY_STATUS = null; + public static String CATEGORY_SYSTEM = null; + public static String CATEGORY_TRANSPORT = null; + public static String EXTRA_AUDIO_CONTENTS_URI = null; + public static String EXTRA_BACKGROUND_IMAGE_URI = null; + public static String EXTRA_BIG_TEXT = null; + public static String EXTRA_CHRONOMETER_COUNT_DOWN = null; + public static String EXTRA_COMPACT_ACTIONS = null; + public static String EXTRA_CONVERSATION_TITLE = null; + public static String EXTRA_HIDDEN_CONVERSATION_TITLE = null; + public static String EXTRA_INFO_TEXT = null; + public static String EXTRA_IS_GROUP_CONVERSATION = null; + public static String EXTRA_LARGE_ICON = null; + public static String EXTRA_LARGE_ICON_BIG = null; + public static String EXTRA_MEDIA_SESSION = null; + public static String EXTRA_MESSAGES = null; + public static String EXTRA_MESSAGING_STYLE_USER = null; + public static String EXTRA_PEOPLE = null; + public static String EXTRA_PICTURE = null; + public static String EXTRA_PROGRESS = null; + public static String EXTRA_PROGRESS_INDETERMINATE = null; + public static String EXTRA_PROGRESS_MAX = null; + public static String EXTRA_REMOTE_INPUT_HISTORY = null; + public static String EXTRA_SELF_DISPLAY_NAME = null; + public static String EXTRA_SHOW_CHRONOMETER = null; + public static String EXTRA_SHOW_WHEN = null; + public static String EXTRA_SMALL_ICON = null; + public static String EXTRA_SUB_TEXT = null; + public static String EXTRA_SUMMARY_TEXT = null; + public static String EXTRA_TEMPLATE = null; + public static String EXTRA_TEXT = null; + public static String EXTRA_TEXT_LINES = null; + public static String EXTRA_TITLE = null; + public static String EXTRA_TITLE_BIG = null; + public static String GROUP_KEY_SILENT = null; + + public static String getCategory(Notification p0) { + return null; + } + + public static String getChannelId(Notification p0) { + return null; + } + + public static String getGroup(Notification p0) { + return null; + } + + public static String getShortcutId(Notification p0) { + return null; + } + + public static String getSortKey(Notification p0) { + return null; + } + + public static boolean getAllowSystemGeneratedContextualActions(Notification p0) { + return false; + } + + public static boolean getLocalOnly(Notification p0) { + return false; + } + + public static boolean isGroupSummary(Notification p0) { + return false; + } + + public static int BADGE_ICON_LARGE = 0; + public static int BADGE_ICON_NONE = 0; + public static int BADGE_ICON_SMALL = 0; + public static int COLOR_DEFAULT = 0; + public static int DEFAULT_ALL = 0; + public static int DEFAULT_LIGHTS = 0; + public static int DEFAULT_SOUND = 0; + public static int DEFAULT_VIBRATE = 0; + public static int FLAG_AUTO_CANCEL = 0; + public static int FLAG_BUBBLE = 0; + public static int FLAG_FOREGROUND_SERVICE = 0; + public static int FLAG_GROUP_SUMMARY = 0; + public static int FLAG_HIGH_PRIORITY = 0; + public static int FLAG_INSISTENT = 0; + public static int FLAG_LOCAL_ONLY = 0; + public static int FLAG_NO_CLEAR = 0; + public static int FLAG_ONGOING_EVENT = 0; + public static int FLAG_ONLY_ALERT_ONCE = 0; + public static int FLAG_SHOW_LIGHTS = 0; + public static int GROUP_ALERT_ALL = 0; + public static int GROUP_ALERT_CHILDREN = 0; + public static int GROUP_ALERT_SUMMARY = 0; + public static int PRIORITY_DEFAULT = 0; + public static int PRIORITY_HIGH = 0; + public static int PRIORITY_LOW = 0; + public static int PRIORITY_MAX = 0; + public static int PRIORITY_MIN = 0; + public static int STREAM_DEFAULT = 0; + public static int VISIBILITY_PRIVATE = 0; + public static int VISIBILITY_PUBLIC = 0; + public static int VISIBILITY_SECRET = 0; + + public static int getActionCount(Notification p0) { + return 0; + } + + public static int getBadgeIconType(Notification p0) { + return 0; + } + + public static int getGroupAlertBehavior(Notification p0) { + return 0; + } + + public static long getTimeoutAfter(Notification p0) { + return 0; + } + + static public class Action { + protected Action() {} + + public Action(IconCompat p0, CharSequence p1, PendingIntent p2) {} + + public Action(int p0, CharSequence p1, PendingIntent p2) {} + + public Bundle getExtras() { + return null; + } + + public CharSequence getTitle() { + return null; + } + + public CharSequence title = null; + + public IconCompat getIconCompat() { + return null; + } + + public PendingIntent actionIntent = null; + + public PendingIntent getActionIntent() { + return null; + } + + public RemoteInput[] getDataOnlyRemoteInputs() { + return null; + } + + public RemoteInput[] getRemoteInputs() { + return null; + } + + public boolean getAllowGeneratedReplies() { + return false; + } + + public boolean getShowsUserInterface() { + return false; + } + + public boolean isContextual() { + return false; + } + + public int getIcon() { + return 0; + } + + public int getSemanticAction() { + return 0; + } + + public int icon = 0; + public static int SEMANTIC_ACTION_ARCHIVE = 0; + public static int SEMANTIC_ACTION_CALL = 0; + public static int SEMANTIC_ACTION_DELETE = 0; + public static int SEMANTIC_ACTION_MARK_AS_READ = 0; + public static int SEMANTIC_ACTION_MARK_AS_UNREAD = 0; + public static int SEMANTIC_ACTION_MUTE = 0; + public static int SEMANTIC_ACTION_NONE = 0; + public static int SEMANTIC_ACTION_REPLY = 0; + public static int SEMANTIC_ACTION_THUMBS_DOWN = 0; + public static int SEMANTIC_ACTION_THUMBS_UP = 0; + public static int SEMANTIC_ACTION_UNMUTE = 0; + + static public class Builder { + protected Builder() {} + + public Builder(IconCompat p0, CharSequence p1, PendingIntent p2) {} + + public Builder(NotificationCompat.Action p0) {} + + public Builder(int p0, CharSequence p1, PendingIntent p2) {} + + public Bundle getExtras() { + return null; + } + + public NotificationCompat.Action build() { + return null; + } + + public NotificationCompat.Action.Builder addExtras(Bundle p0) { + return null; + } + + public NotificationCompat.Action.Builder addRemoteInput(RemoteInput p0) { + return null; + } + + public NotificationCompat.Action.Builder extend(NotificationCompat.Action.Extender p0) { + return null; + } + + public NotificationCompat.Action.Builder setAllowGeneratedReplies(boolean p0) { + return null; + } + + public NotificationCompat.Action.Builder setContextual(boolean p0) { + return null; + } + + public NotificationCompat.Action.Builder setSemanticAction(int p0) { + return null; + } + + public NotificationCompat.Action.Builder setShowsUserInterface(boolean p0) { + return null; + } + } + static public interface Extender { + NotificationCompat.Action.Builder extend(NotificationCompat.Action.Builder p0); + } + } + static public class BigPictureStyle extends NotificationCompat.Style { + public BigPictureStyle() {} + + public BigPictureStyle(NotificationCompat.Builder p0) {} + + public NotificationCompat.BigPictureStyle bigLargeIcon(Bitmap p0) { + return null; + } + + public NotificationCompat.BigPictureStyle bigPicture(Bitmap p0) { + return null; + } + + public NotificationCompat.BigPictureStyle setBigContentTitle(CharSequence p0) { + return null; + } + + public NotificationCompat.BigPictureStyle setSummaryText(CharSequence p0) { + return null; + } + + public void apply(NotificationBuilderWithBuilderAccessor p0) {} + } + static public class BigTextStyle extends NotificationCompat.Style { + public BigTextStyle() {} + + public BigTextStyle(NotificationCompat.Builder p0) {} + + public NotificationCompat.BigTextStyle bigText(CharSequence p0) { + return null; + } + + public NotificationCompat.BigTextStyle setBigContentTitle(CharSequence p0) { + return null; + } + + public NotificationCompat.BigTextStyle setSummaryText(CharSequence p0) { + return null; + } + + public void apply(NotificationBuilderWithBuilderAccessor p0) {} + } + static public class BubbleMetadata { + protected BubbleMetadata() {} + + public IconCompat getIcon() { + return null; + } + + public PendingIntent getDeleteIntent() { + return null; + } + + public PendingIntent getIntent() { + return null; + } + + public boolean getAutoExpandBubble() { + return false; + } + + public boolean isNotificationSuppressed() { + return false; + } + + public int getDesiredHeight() { + return 0; + } + + public int getDesiredHeightResId() { + return 0; + } + + public static Notification.BubbleMetadata toPlatform(NotificationCompat.BubbleMetadata p0) { + return null; + } + + public static NotificationCompat.BubbleMetadata fromPlatform( + Notification.BubbleMetadata p0) { + return null; + } + } + static public class Builder { + protected Builder() {} + + protected static CharSequence limitCharSequenceLength(CharSequence p0) { + return null; + } + + public ArrayList mActions = null; + public ArrayList mPeople = null; + + public Builder(Context p0) {} + + public Builder(Context p0, String p1) {} + + public Bundle getExtras() { + return null; + } + + public Context mContext = null; + + public Notification build() { + return null; + } + + public Notification getNotification() { + return null; + } + + public NotificationCompat.BubbleMetadata getBubbleMetadata() { + return null; + } + + public NotificationCompat.Builder addAction(NotificationCompat.Action p0) { + return null; + } + + public NotificationCompat.Builder addAction(int p0, CharSequence p1, PendingIntent p2) { + return null; + } + + public NotificationCompat.Builder addExtras(Bundle p0) { + return null; + } + + public NotificationCompat.Builder addInvisibleAction(NotificationCompat.Action p0) { + return null; + } + + public NotificationCompat.Builder addInvisibleAction(int p0, CharSequence p1, + PendingIntent p2) { + return null; + } + + public NotificationCompat.Builder addPerson(String p0) { + return null; + } + + public NotificationCompat.Builder extend(NotificationCompat.Extender p0) { + return null; + } + + public NotificationCompat.Builder setAllowSystemGeneratedContextualActions(boolean p0) { + return null; + } + + public NotificationCompat.Builder setAutoCancel(boolean p0) { + return null; + } + + public NotificationCompat.Builder setBadgeIconType(int p0) { + return null; + } + + public NotificationCompat.Builder setBubbleMetadata(NotificationCompat.BubbleMetadata p0) { + return null; + } + + public NotificationCompat.Builder setCategory(String p0) { + return null; + } + + public NotificationCompat.Builder setChannelId(String p0) { + return null; + } + + public NotificationCompat.Builder setChronometerCountDown(boolean p0) { + return null; + } + + public NotificationCompat.Builder setColor(int p0) { + return null; + } + + public NotificationCompat.Builder setColorized(boolean p0) { + return null; + } + + public NotificationCompat.Builder setContent(RemoteViews p0) { + return null; + } + + public NotificationCompat.Builder setContentInfo(CharSequence p0) { + return null; + } + + public NotificationCompat.Builder setContentIntent(PendingIntent p0) { + return null; + } + + public NotificationCompat.Builder setContentText(CharSequence p0) { + return null; + } + + public NotificationCompat.Builder setContentTitle(CharSequence p0) { + return null; + } + + public NotificationCompat.Builder setCustomBigContentView(RemoteViews p0) { + return null; + } + + public NotificationCompat.Builder setCustomContentView(RemoteViews p0) { + return null; + } + + public NotificationCompat.Builder setCustomHeadsUpContentView(RemoteViews p0) { + return null; + } + + public NotificationCompat.Builder setDefaults(int p0) { + return null; + } + + public NotificationCompat.Builder setDeleteIntent(PendingIntent p0) { + return null; + } + + public NotificationCompat.Builder setExtras(Bundle p0) { + return null; + } + + public NotificationCompat.Builder setFullScreenIntent(PendingIntent p0, boolean p1) { + return null; + } + + public NotificationCompat.Builder setGroup(String p0) { + return null; + } + + public NotificationCompat.Builder setGroupAlertBehavior(int p0) { + return null; + } + + public NotificationCompat.Builder setGroupSummary(boolean p0) { + return null; + } + + public NotificationCompat.Builder setLargeIcon(Bitmap p0) { + return null; + } + + public NotificationCompat.Builder setLights(int p0, int p1, int p2) { + return null; + } + + public NotificationCompat.Builder setLocalOnly(boolean p0) { + return null; + } + + public NotificationCompat.Builder setNotificationSilent() { + return null; + } + + public NotificationCompat.Builder setNumber(int p0) { + return null; + } + + public NotificationCompat.Builder setOngoing(boolean p0) { + return null; + } + + public NotificationCompat.Builder setOnlyAlertOnce(boolean p0) { + return null; + } + + public NotificationCompat.Builder setPriority(int p0) { + return null; + } + + public NotificationCompat.Builder setProgress(int p0, int p1, boolean p2) { + return null; + } + + public NotificationCompat.Builder setPublicVersion(Notification p0) { + return null; + } + + public NotificationCompat.Builder setRemoteInputHistory(CharSequence[] p0) { + return null; + } + + public NotificationCompat.Builder setShortcutId(String p0) { + return null; + } + + public NotificationCompat.Builder setShowWhen(boolean p0) { + return null; + } + + public NotificationCompat.Builder setSmallIcon(int p0) { + return null; + } + + public NotificationCompat.Builder setSmallIcon(int p0, int p1) { + return null; + } + + public NotificationCompat.Builder setSortKey(String p0) { + return null; + } + + public NotificationCompat.Builder setSound(Uri p0) { + return null; + } + + public NotificationCompat.Builder setSound(Uri p0, int p1) { + return null; + } + + public NotificationCompat.Builder setStyle(NotificationCompat.Style p0) { + return null; + } + + public NotificationCompat.Builder setSubText(CharSequence p0) { + return null; + } + + public NotificationCompat.Builder setTicker(CharSequence p0) { + return null; + } + + public NotificationCompat.Builder setTicker(CharSequence p0, RemoteViews p1) { + return null; + } + + public NotificationCompat.Builder setTimeoutAfter(long p0) { + return null; + } + + public NotificationCompat.Builder setUsesChronometer(boolean p0) { + return null; + } + + public NotificationCompat.Builder setVibrate(long[] p0) { + return null; + } + + public NotificationCompat.Builder setVisibility(int p0) { + return null; + } + + public NotificationCompat.Builder setWhen(long p0) { + return null; + } + + public RemoteViews getBigContentView() { + return null; + } + + public RemoteViews getContentView() { + return null; + } + + public RemoteViews getHeadsUpContentView() { + return null; + } + + public int getColor() { + return 0; + } + + public int getPriority() { + return 0; + } + + public long getWhenIfShowing() { + return 0; + } + } + static public class InboxStyle extends NotificationCompat.Style { + public InboxStyle() {} + + public InboxStyle(NotificationCompat.Builder p0) {} + + public NotificationCompat.InboxStyle addLine(CharSequence p0) { + return null; + } + + public NotificationCompat.InboxStyle setBigContentTitle(CharSequence p0) { + return null; + } + + public NotificationCompat.InboxStyle setSummaryText(CharSequence p0) { + return null; + } + + public void apply(NotificationBuilderWithBuilderAccessor p0) {} + } + static public interface Extender { + NotificationCompat.Builder extend(NotificationCompat.Builder p0); + } +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationManagerCompat.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationManagerCompat.java new file mode 100644 index 00000000000..184c3cd25c7 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/NotificationManagerCompat.java @@ -0,0 +1,43 @@ +// Generated automatically from androidx.core.app.NotificationManagerCompat for testing purposes + +package androidx.core.app; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.content.Context; +import java.util.List; +import java.util.Set; + +public class NotificationManagerCompat +{ + protected NotificationManagerCompat() {} + public List getNotificationChannels(){ return null; } + public List getNotificationChannelGroups(){ return null; } + public NotificationChannel getNotificationChannel(String p0){ return null; } + public NotificationChannelGroup getNotificationChannelGroup(String p0){ return null; } + public boolean areNotificationsEnabled(){ return false; } + public int getImportance(){ return 0; } + public static NotificationManagerCompat from(Context p0){ return null; } + public static Set getEnabledListenerPackages(Context p0){ return null; } + public static String ACTION_BIND_SIDE_CHANNEL = null; + public static String EXTRA_USE_SIDE_CHANNEL = null; + public static int IMPORTANCE_DEFAULT = 0; + public static int IMPORTANCE_HIGH = 0; + public static int IMPORTANCE_LOW = 0; + public static int IMPORTANCE_MAX = 0; + public static int IMPORTANCE_MIN = 0; + public static int IMPORTANCE_NONE = 0; + public static int IMPORTANCE_UNSPECIFIED = 0; + public void cancel(String p0, int p1){} + public void cancel(int p0){} + public void cancelAll(){} + public void createNotificationChannel(NotificationChannel p0){} + public void createNotificationChannelGroup(NotificationChannelGroup p0){} + public void createNotificationChannelGroups(List p0){} + public void createNotificationChannels(List p0){} + public void deleteNotificationChannel(String p0){} + public void deleteNotificationChannelGroup(String p0){} + public void notify(String p0, int p1, Notification p2){} + public void notify(int p0, Notification p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/RemoteInput.java b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/RemoteInput.java new file mode 100644 index 00000000000..8f684095110 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/core/app/RemoteInput.java @@ -0,0 +1,71 @@ +// Generated automatically from androidx.core.app.RemoteInput for testing purposes + +package androidx.core.app; + +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import java.util.Map; +import java.util.Set; + +public class RemoteInput { + protected RemoteInput() {} + + public Bundle getExtras() { + return null; + } + + public CharSequence getLabel() { + return null; + } + + public CharSequence[] getChoices() { + return null; + } + + public Set getAllowedDataTypes() { + return null; + } + + public String getResultKey() { + return null; + } + + public boolean getAllowFreeFormInput() { + return false; + } + + public boolean isDataOnly() { + return false; + } + + public int getEditChoicesBeforeSending() { + return 0; + } + + public static Bundle getResultsFromIntent(Intent p0) { + return null; + } + + public static Map getDataResultsFromIntent(Intent p0, String p1) { + return null; + } + + public static String EXTRA_RESULTS_DATA = null; + public static String RESULTS_CLIP_LABEL = null; + public static int EDIT_CHOICES_BEFORE_SENDING_AUTO = 0; + public static int EDIT_CHOICES_BEFORE_SENDING_DISABLED = 0; + public static int EDIT_CHOICES_BEFORE_SENDING_ENABLED = 0; + public static int SOURCE_CHOICE = 0; + public static int SOURCE_FREE_FORM_INPUT = 0; + + public static int getResultsSource(Intent p0) { + return 0; + } + + public static void addDataResultToIntent(RemoteInput p0, Intent p1, Map p2) {} + + public static void addResultsToIntent(RemoteInput[] p0, Intent p1, Bundle p2) {} + + public static void setResultsSource(Intent p0, int p1) {} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/remotecallback/CallbackReceiver.java b/java/ql/test/stubs/google-android-9.0.0/androidx/remotecallback/CallbackReceiver.java new file mode 100644 index 00000000000..3215fe270d8 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/remotecallback/CallbackReceiver.java @@ -0,0 +1,10 @@ +// Generated automatically from androidx.remotecallback.CallbackReceiver for testing purposes + +package androidx.remotecallback; + +import android.content.Context; + +public interface CallbackReceiver +{ + T createRemoteCallback(Context p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/slice/Clock.java b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/Clock.java new file mode 100644 index 00000000000..4aed87209c6 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/Clock.java @@ -0,0 +1,9 @@ +// Generated automatically from androidx.slice.Clock for testing purposes + +package androidx.slice; + + +public interface Clock +{ + long currentTimeMillis(); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/slice/builders/impl/TemplateBuilderImpl.java b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/builders/impl/TemplateBuilderImpl.java new file mode 100644 index 00000000000..073ce2604c9 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/builders/impl/TemplateBuilderImpl.java @@ -0,0 +1,23 @@ +// Generated automatically from androidx.slice.builders.impl.TemplateBuilderImpl for testing purposes + +package androidx.slice.builders.impl; + +import androidx.slice.Clock; +import androidx.slice.Slice; +import androidx.slice.SliceSpec; +import java.util.ArrayList; + +abstract public class TemplateBuilderImpl +{ + protected TemplateBuilderImpl() {} + protected ArrayList parseImageMode(int p0, boolean p1){ return null; } + protected TemplateBuilderImpl(Slice.Builder p0, SliceSpec p1){} + protected TemplateBuilderImpl(Slice.Builder p0, SliceSpec p1, Clock p2){} + protected void setBuilder(Slice.Builder p0){} + public Clock getClock(){ return null; } + public Slice build(){ return null; } + public Slice.Builder createChildBuilder(){ return null; } + public Slice.Builder getBuilder(){ return null; } + public SliceSpec getSpec(){ return null; } + public abstract void apply(Slice.Builder p0); +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/slice/compat/CompatPermissionManager.java b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/compat/CompatPermissionManager.java new file mode 100644 index 00000000000..a04e2c809cd --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/slice/compat/CompatPermissionManager.java @@ -0,0 +1,16 @@ +// Generated automatically from androidx.slice.compat.CompatPermissionManager for testing purposes + +package androidx.slice.compat; + +import android.content.Context; +import android.net.Uri; + +public class CompatPermissionManager +{ + protected CompatPermissionManager() {} + public CompatPermissionManager(Context p0, String p1, int p2, String[] p3){} + public int checkSlicePermission(Uri p0, int p1, int p2){ return 0; } + public static String ALL_SUFFIX = null; + public void grantSlicePermission(Uri p0, String p1){} + public void revokeSlicePermission(Uri p0, String p1){} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/CustomVersionedParcelable.java b/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/CustomVersionedParcelable.java new file mode 100644 index 00000000000..8ffcfef8989 --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/CustomVersionedParcelable.java @@ -0,0 +1,14 @@ +// Generated automatically from androidx.versionedparcelable.CustomVersionedParcelable for testing +// purposes + +package androidx.versionedparcelable; + +import androidx.versionedparcelable.VersionedParcelable; + +abstract public class CustomVersionedParcelable implements VersionedParcelable { + public CustomVersionedParcelable() {} + + public void onPostParceling() {} + + public void onPreParceling(boolean p0) {} +} diff --git a/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/VersionedParcelable.java b/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/VersionedParcelable.java new file mode 100644 index 00000000000..866f2e392ab --- /dev/null +++ b/java/ql/test/stubs/google-android-9.0.0/androidx/versionedparcelable/VersionedParcelable.java @@ -0,0 +1,8 @@ +// Generated automatically from androidx.versionedparcelable.VersionedParcelable for testing +// purposes + +package androidx.versionedparcelable; + + +public interface VersionedParcelable { +} diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll index 3acb5c315f7..ed1da2dc38b 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/NosqlInjectionATM.qll @@ -42,10 +42,10 @@ module SinkEndpointFilter { result = "modeled database access" or // Remove calls to APIs that aren't relevant to NoSQL injection - call.getReceiver() instanceof HTTP::RequestNode and + call.getReceiver() instanceof Http::RequestNode and result = "receiver is a HTTP request expression" or - call.getReceiver() instanceof HTTP::ResponseNode and + call.getReceiver() instanceof Http::ResponseNode and result = "receiver is a HTTP response expression" ) or diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index d7698ed15b9..b437e4de0ba 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -1,5 +1,5 @@ name: codeql/javascript-experimental-atm-lib -version: 0.3.3 +version: 0.3.4 extractor: javascript library: true groups: diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index 8c6ac3ea38f..0942785b20a 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -1,6 +1,6 @@ name: codeql/javascript-experimental-atm-queries language: javascript -version: 0.3.3 +version: 0.3.4 suites: codeql-suites defaultSuiteFile: codeql-suites/javascript-atm-code-scanning.qls groups: diff --git a/javascript/ql/lib/change-notes/2022-09-12-uppercase.md b/javascript/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/javascript/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll index 909d5d22f98..72d5a50cebe 100644 --- a/javascript/ql/lib/semmle/javascript/ApiGraphs.qll +++ b/javascript/ql/lib/semmle/javascript/ApiGraphs.qll @@ -652,7 +652,7 @@ module API { exports(m, _, _) or exists(NodeModule nm | nm = mod | - exists(SSA::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()])) + exists(Ssa::implicitInit([nm.getModuleVariable(), nm.getExportsVariable()])) ) ) } or diff --git a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll index 7660d374216..32c1e453444 100644 --- a/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll +++ b/javascript/ql/lib/semmle/javascript/NodeModuleResolutionImpl.qll @@ -90,10 +90,11 @@ bindingset[name] private string getStem(string name) { result = name.regexpCapture("(.+?)(?:\\.([^.]+))?", 1) } /** - * Gets the main module described by `pkg` with the given `priority`. + * Gets a file that a main module from `pkg` exported as `mainPath` with the given `priority`. + * `mainPath` is "." if it's the main module of the package. */ -File resolveMainModule(PackageJson pkg, int priority) { - exists(PathExpr main | main = MainModulePath::of(pkg, ".") | +private File resolveMainPath(PackageJson pkg, string mainPath, int priority) { + exists(PathExpr main | main = MainModulePath::of(pkg, mainPath) | result = main.resolve() and priority = 0 or result = tryExtensions(main.resolve(), "index", priority) @@ -102,6 +103,26 @@ File resolveMainModule(PackageJson pkg, int priority) { exists(int n | n = main.getNumComponent() | result = tryExtensions(main.resolveUpTo(n - 1), getStem(main.getComponent(n - 1)), priority) ) + or + // assuming the files get moved from one dir to another during compilation: + not exists(main.resolve()) and // didn't resolve + count(int i, string comp | comp = main.getComponent(i) and not comp = "." | i) = 2 and // is down one folder + exists(Folder subFolder | subFolder = pkg.getFile().getParentContainer().getAFolder() | + // is in one folder below the package.json, and has the right basename + result = + tryExtensions(subFolder, getStem(main.getComponent(main.getNumComponent() - 1)), + priority - 999) // very high priority, to make sure everything else is tried first + ) + ) +} + +/** + * Gets the main module described by `pkg` with the given `priority`. + */ +File resolveMainModule(PackageJson pkg, int priority) { + exists(int subPriority, string mainPath | + result = resolveMainPath(pkg, mainPath, subPriority) and + if mainPath = "." then subPriority = priority else priority = subPriority + 1000 ) or exists(Folder folder, Folder child | diff --git a/javascript/ql/lib/semmle/javascript/PackageExports.qll b/javascript/ql/lib/semmle/javascript/PackageExports.qll index 01cd134ff0c..909564f740b 100644 --- a/javascript/ql/lib/semmle/javascript/PackageExports.qll +++ b/javascript/ql/lib/semmle/javascript/PackageExports.qll @@ -52,6 +52,16 @@ private DataFlow::Node getAValueExportedByPackage() { not isPrivateMethodDeclaration(result) ) or + // module.exports.foo = function () { + // return new Foo(); // <- result + // }; + exists(DataFlow::FunctionNode func, DataFlow::NewNode inst, DataFlow::ClassNode clz | + func = getAValueExportedByPackage().getALocalSource() and inst = unique( | | func.getAReturn()) + | + clz.getAnInstanceReference() = inst and + result = clz.getAnInstanceMember(_) + ) + or result = getAValueExportedByPackage().getALocalSource() or // Nested property reads. diff --git a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll index 9d8b3967b1c..a385df662ce 100644 --- a/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll +++ b/javascript/ql/lib/semmle/javascript/RangeAnalysis.qll @@ -103,7 +103,7 @@ module RangeAnalysis { * the given increment/decrement expression. */ private DataFlow::Node updateExprResult(UpdateExpr expr) { - result = DataFlow::ssaDefinitionNode(SSA::definition(expr)) + result = DataFlow::ssaDefinitionNode(Ssa::definition(expr)) or expr.isPrefix() and result = expr.flow() @@ -113,7 +113,7 @@ module RangeAnalysis { * Gets a data flow node holding the result of the given componund assignment. */ private DataFlow::Node compoundAssignResult(CompoundAssignExpr expr) { - result = DataFlow::ssaDefinitionNode(SSA::definition(expr)) + result = DataFlow::ssaDefinitionNode(Ssa::definition(expr)) or result = expr.flow() } diff --git a/javascript/ql/lib/semmle/javascript/Routing.qll b/javascript/ql/lib/semmle/javascript/Routing.qll index 96d3e0a7f51..a7e37c10743 100644 --- a/javascript/ql/lib/semmle/javascript/Routing.qll +++ b/javascript/ql/lib/semmle/javascript/Routing.qll @@ -282,7 +282,7 @@ module Routing { * Gets an HTTP method name which this node will accept, or nothing if the node accepts all HTTP methods, not * taking into account the context from ancestors or children nodes. */ - HTTP::RequestMethodName getOwnHttpMethod() { none() } // Overridden in subclass + Http::RequestMethodName getOwnHttpMethod() { none() } // Overridden in subclass private Node getAUseSiteInRouteSetup() { if this.getParent() instanceof RouteSetup @@ -383,7 +383,7 @@ module Routing { * Gets an HTTP request method name (in upper case) matched by this node, or nothing * if all HTTP request method names are accepted. */ - HTTP::RequestMethodName getHttpMethod() { none() } + Http::RequestMethodName getHttpMethod() { none() } } private class ValueNodeImpl extends Node, MkValueNode { @@ -407,7 +407,7 @@ module Routing { override string getRelativePath() { result = range.getRelativePath() } - override HTTP::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() } + override Http::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() } } private StepSummary routeStepSummary() { @@ -434,7 +434,7 @@ module Routing { or StepSummary::smallstep(result, this, routeStepSummary()) or - HTTP::routeHandlerStep(result, this) + Http::routeHandlerStep(result, this) or RouteHandlerTrackingStep::step(result, this) or @@ -599,7 +599,7 @@ module Routing { * Gets an HTTP request method name (in upper case) matched by this node, or nothing * if all HTTP request method names are accepted. */ - HTTP::RequestMethodName getHttpMethod() { none() } + Http::RequestMethodName getHttpMethod() { none() } /** * Holds if this route setup targets `router` and occurs at the given `cfgNode`. @@ -635,7 +635,7 @@ module Routing { override string getRelativePath() { result = range.getRelativePath() } - override HTTP::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() } + override Http::RequestMethodName getOwnHttpMethod() { result = range.getHttpMethod() } } /** diff --git a/javascript/ql/lib/semmle/javascript/SSA.qll b/javascript/ql/lib/semmle/javascript/SSA.qll index 8e60fb0c3e4..3ba64d0200d 100644 --- a/javascript/ql/lib/semmle/javascript/SSA.qll +++ b/javascript/ql/lib/semmle/javascript/SSA.qll @@ -737,7 +737,7 @@ class SsaRefinementNode extends SsaPseudoDefinition, TRefinement { } } -module SSA { +module Ssa { /** Gets the SSA definition corresponding to the implicit initialization of `v`. */ SsaImplicitInit implicitInit(SsaSourceVariable v) { result.getSourceVariable() = v } @@ -747,3 +747,6 @@ module SSA { /** Gets the SSA variable corresponding to `d`. */ SsaVariable variable(VarDef d) { result.getDefinition() = definition(d) } } + +/** DEPRECATED: Alias for Ssa */ +deprecated module SSA = Ssa; diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index 9dcf6bb1913..2449f8a8eb8 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -770,7 +770,7 @@ private class FlowStepThroughImport extends SharedFlowStep { override predicate step(DataFlow::Node pred, DataFlow::Node succ) { exists(ImportSpecifier specifier | pred = DataFlow::valueNode(specifier) and - succ = DataFlow::ssaDefinitionNode(SSA::definition(specifier)) + succ = DataFlow::ssaDefinitionNode(Ssa::definition(specifier)) ) } } @@ -1777,7 +1777,7 @@ class MidPathNode extends PathNode, MkMidNode { SsaImplicitDefinition or // Skip SSA definition of parameter as its location coincides with the parameter node - nd = DataFlow::ssaDefinitionNode(SSA::definition(any(SimpleParameter p))) + nd = DataFlow::ssaDefinitionNode(Ssa::definition(any(SimpleParameter p))) or // Skip to the top of big left-leaning string concatenation trees. nd = any(AddExpr add).flow() and diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll index c3cf7ab15a4..6e7f8bc3461 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Sources.qll @@ -348,7 +348,7 @@ private class NodeModuleSourcesNodes extends SourceNode::Range { NodeModuleSourcesNodes() { exists(NodeModule m | - this = DataFlow::ssaDefinitionNode(SSA::implicitInit(v)) and + this = DataFlow::ssaDefinitionNode(Ssa::implicitInit(v)) and v = [m.getModuleVariable(), m.getExportsVariable()] ) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll index e721508ff06..f570c9d3a38 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ClientRequests.qll @@ -112,7 +112,7 @@ module ClientRequest { /** * Gets the name of an HTTP request method, in all-lowercase. */ - private string httpMethodName() { result = any(HTTP::RequestMethodName m).toLowerCase() } + private string httpMethodName() { result = any(Http::RequestMethodName m).toLowerCase() } /** * Gets a model of an instance of the `request` library, or one of diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Connect.qll b/javascript/ql/lib/semmle/javascript/frameworks/Connect.qll index fe130648cd5..59518a2f4f3 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Connect.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Connect.qll @@ -10,7 +10,7 @@ module Connect { /** * An expression that creates a new Connect server. */ - class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::CallNode { + class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::CallNode { ServerDefinition() { // `app = connect()` this = DataFlow::moduleImport("connect").getAnInvocation() @@ -61,7 +61,7 @@ module Connect { /** * A call to a Connect method that sets up a route. */ - class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup { + class RouteSetup extends DataFlow::MethodCallNode, Http::Servers::StandardRouteSetup { ServerDefinition server; RouteSetup() { @@ -125,7 +125,7 @@ module Connect { /** * An access to a user-controlled Connect request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess instanceof DataFlow::MethodCallNode { + private class RequestInputAccess extends Http::RequestInputAccess instanceof DataFlow::MethodCallNode { RequestNode request; string kind; diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll index f0ea2f03f68..87956ef38c8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ConnectExpressShared.qll @@ -115,7 +115,7 @@ module ConnectExpressShared { * * For example, this could be the function `function(req, res, next){...}`. */ - class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate { + class RouteHandlerCandidate extends Http::RouteHandlerCandidate { RouteHandlerCandidate() { matchesSignature(this, _) and not ( diff --git a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll index 1efc7da5dc4..b78fd0834db 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/CookieLibraries.qll @@ -368,7 +368,7 @@ private class HttpCookieWrite extends CookieWrites::CookieWrite { string header; HttpCookieWrite() { - exists(HTTP::CookieDefinition setCookie | + exists(Http::CookieDefinition setCookie | this = setCookie.getHeaderArgument() and not this instanceof DataFlow::ArrayCreationNode or diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll index 86abe01f5d6..a48b7d1719a 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Express.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Express.qll @@ -70,7 +70,7 @@ module Express { result = "param" or result = "all" or result = "use" or - result = any(HTTP::RequestMethodName m).toLowerCase() or + result = any(Http::RequestMethodName m).toLowerCase() or // deprecated methods result = "error" or result = "del" @@ -92,7 +92,7 @@ module Express { result = this.getArgument(0).getStringValue() } - override HTTP::RequestMethodName getHttpMethod() { result.toLowerCase() = this.getMethodName() } + override Http::RequestMethodName getHttpMethod() { result.toLowerCase() = this.getMethodName() } } /** @@ -136,7 +136,7 @@ module Express { /** * A call to an Express router method that sets up a route. */ - class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::MethodCallNode { + class RouteSetup extends Http::Servers::StandardRouteSetup, DataFlow::MethodCallNode { RouteSetup() { isRouter(this.getReceiver()) and this.getMethodName() = routeSetupMethodName() @@ -219,7 +219,7 @@ module Express { | result = succ.backtrack(t2, t) or - HTTP::routeHandlerStep(result, succ) and + Http::routeHandlerStep(result, succ) and t = t2 ) } @@ -233,7 +233,7 @@ module Express { * * Has no result for `use`, `all`, or `param` calls. */ - HTTP::RequestMethodName getRequestMethod() { result.toLowerCase() = this.getMethodName() } + Http::RequestMethodName getRequestMethod() { result.toLowerCase() = this.getMethodName() } /** * Holds if this registers a route for all request methods. @@ -260,7 +260,7 @@ module Express { /** * A call that sets up a Passport router that includes the request object. */ - private class PassportRouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::CallNode { + private class PassportRouteSetup extends Http::Servers::StandardRouteSetup, DataFlow::CallNode { DataFlow::ModuleImportNode importNode; DataFlow::FunctionNode callback; @@ -285,7 +285,7 @@ module Express { /** * The callback given to passport in PassportRouteSetup. */ - private class PassportRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler, + private class PassportRouteHandler extends RouteHandler, Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { PassportRouteHandler() { this = any(PassportRouteSetup setup).getARouteHandler() } @@ -470,7 +470,7 @@ module Express { * but support for other kinds of route handlers can be added by implementing * additional subclasses of this class. */ - abstract class RouteHandler extends HTTP::RouteHandler { + abstract class RouteHandler extends Http::RouteHandler { /** * Gets the parameter of kind `kind` of this route handler. * @@ -501,7 +501,7 @@ module Express { /** * An Express route handler installed by a route setup. */ - class StandardRouteHandler extends RouteHandler, HTTP::Servers::StandardRouteHandler, + class StandardRouteHandler extends RouteHandler, Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { RouteSetup routeSetup; @@ -530,7 +530,7 @@ module Express { } /** An Express response source. */ - abstract class ResponseSource extends HTTP::Servers::ResponseSource { } + abstract class ResponseSource extends Http::Servers::ResponseSource { } /** * An Express response source, that is, the response parameter of a @@ -561,7 +561,7 @@ module Express { } /** An Express request source. */ - abstract class RequestSource extends HTTP::Servers::RequestSource { } + abstract class RequestSource extends Http::Servers::RequestSource { } /** * An Express request source, that is, the request parameter of a @@ -632,7 +632,7 @@ module Express { } /** The input parameter to an `app.param()` route handler. */ - private class ParamHandlerInputAccess extends HTTP::RequestInputAccess { + private class ParamHandlerInputAccess extends Http::RequestInputAccess { RouteHandler rh; ParamHandlerInputAccess() { @@ -641,7 +641,7 @@ module Express { ) } - override HTTP::RouteHandler getRouteHandler() { result = rh } + override Http::RouteHandler getRouteHandler() { result = rh } override string getKind() { result = "parameter" } } @@ -675,7 +675,7 @@ module Express { /** * An access to a user-controlled Express request input. */ - class RequestInputAccess extends HTTP::RequestInputAccess { + class RequestInputAccess extends Http::RequestInputAccess { RequestSource request; string kind; @@ -733,7 +733,7 @@ module Express { /** * An access to a header on an Express request. */ - private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess { RequestSource request; RequestHeaderAccess() { @@ -762,7 +762,7 @@ module Express { /** * HTTP headers created by Express calls */ - abstract private class ExplicitHeader extends HTTP::ExplicitHeaderDefinition { } + abstract private class ExplicitHeader extends Http::ExplicitHeaderDefinition { } /** * Holds if `e` is an HTTP request object. @@ -781,7 +781,7 @@ module Express { RequestBodyAccess() { any(RouteHandler h).getARequestBodyAccess() = this } } - abstract private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition { + abstract private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { HeaderDefinition() { isResponse(this.getReceiver()) } override RouteHandler getRouteHandler() { this.getReceiver() = result.getAResponseNode() } @@ -790,7 +790,7 @@ module Express { /** * An invocation of the `redirect` method of an HTTP response object. */ - private class RedirectInvocation extends HTTP::RedirectInvocation, DataFlow::MethodCallNode { + private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode { ResponseSource response; RedirectInvocation() { this = response.ref().getAMethodCall("redirect") } @@ -854,7 +854,7 @@ module Express { /** * An argument passed to the `send` or `end` method of an HTTP response object. */ - private class ResponseSendArgument extends HTTP::ResponseSendArgument { + private class ResponseSendArgument extends Http::ResponseSendArgument { ResponseSource response; ResponseSendArgument() { this = response.ref().getAMethodCall("send").getArgument(0) } @@ -865,7 +865,7 @@ module Express { /** * An invocation of the `cookie` method on an HTTP response object. */ - class SetCookie extends HTTP::CookieDefinition, DataFlow::MethodCallNode { + class SetCookie extends Http::CookieDefinition, DataFlow::MethodCallNode { ResponseSource response; SetCookie() { this = response.ref().getAMethodCall("cookie") } @@ -881,7 +881,7 @@ module Express { * An expression passed to the `render` method of an HTTP response object * as the value of a template variable. */ - private class TemplateInput extends HTTP::ResponseBody { + private class TemplateInput extends Http::ResponseBody { TemplateObjectInput obj; TemplateInput() { @@ -913,13 +913,13 @@ module Express { /** * An Express server application. */ - private class Application extends HTTP::ServerDefinition { + private class Application extends Http::ServerDefinition { Application() { this = appCreation() } /** * Gets a route handler of the application, regardless of nesting. */ - override HTTP::RouteHandler getARouteHandler() { + override Http::RouteHandler getARouteHandler() { result = this.(RouterDefinition).getASubRouter*().getARouteHandler() } } @@ -960,7 +960,7 @@ module Express { * * Example: `fun` for `router1.use(fun)` or `router.use("/route", fun)` */ - HTTP::RouteHandler getARouteHandler() { + Http::RouteHandler getARouteHandler() { result.(DataFlow::SourceNode).flowsTo(this.getARouteSetup().getAnArgument()) } @@ -1044,7 +1044,7 @@ module Express { * A function that flows to a route setup. */ private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler, - HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { RouteSetup routeSetup; TrackedRouteHandlerCandidateWithSetup() { this = routeSetup.getARouteHandler() } @@ -1063,14 +1063,14 @@ module Express { * `router.post(handler)` where it is unknown if `router` is an * Express router. */ - class RouteSetupCandidate extends HTTP::RouteSetupCandidate, DataFlow::MethodCallNode { + class RouteSetupCandidate extends Http::RouteSetupCandidate, DataFlow::MethodCallNode { DataFlow::ValueNode routeHandlerArg; RouteSetupCandidate() { exists(string methodName | methodName = "all" or methodName = "use" or - methodName = any(HTTP::RequestMethodName m).toLowerCase() + methodName = any(Http::RequestMethodName m).toLowerCase() | this.getMethodName() = methodName and exists(DataFlow::ValueNode arg | arg = this.getAnArgument() | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/ExpressModules.qll b/javascript/ql/lib/semmle/javascript/frameworks/ExpressModules.qll index 64b4122fb46..6052d97cef8 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/ExpressModules.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/ExpressModules.qll @@ -18,25 +18,25 @@ module ExpressLibraries { /** * A header produced by a route handler of the "x-frame-options" module. */ - class XFrameOptionsRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition { + class XFrameOptionsRouteHandlerHeader extends Http::ImplicitHeaderDefinition { XFrameOptionsRouteHandlerHeader() { this instanceof XFrameOptionsRouteHandler } override predicate defines(string headerName, string headerValue) { xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue) } - override HTTP::RouteHandler getRouteHandler() { result = this } + override Http::RouteHandler getRouteHandler() { result = this } } /** * A route handler from the "x-frame-options" module. */ - class XFrameOptionsRouteHandler extends HTTP::RouteHandler { + class XFrameOptionsRouteHandler extends Http::RouteHandler { XFrameOptionsRouteHandler() { this = DataFlow::moduleImport("x-frame-options").getAnInvocation() } - override HTTP::HeaderDefinition getAResponseHeader(string name) { + override Http::HeaderDefinition getAResponseHeader(string name) { name = this.(XFrameOptionsRouteHandlerHeader).getAHeaderName() and result = this } @@ -45,23 +45,23 @@ module ExpressLibraries { /** * A header produced by a route handler of the "frameguard" module. */ - class FrameGuardRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition { + class FrameGuardRouteHandlerHeader extends Http::ImplicitHeaderDefinition { FrameGuardRouteHandlerHeader() { this instanceof FrameGuardRouteHandler } override predicate defines(string headerName, string headerValue) { xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue) } - override HTTP::RouteHandler getRouteHandler() { result = this } + override Http::RouteHandler getRouteHandler() { result = this } } /** * A route handler from the "frameguard" module. */ - class FrameGuardRouteHandler extends HTTP::RouteHandler { + class FrameGuardRouteHandler extends Http::RouteHandler { FrameGuardRouteHandler() { this = DataFlow::moduleImport("frameguard").getAnInvocation() } - override HTTP::HeaderDefinition getAResponseHeader(string name) { + override Http::HeaderDefinition getAResponseHeader(string name) { name = this.(FrameGuardRouteHandlerHeader).getAHeaderName() and result = this } @@ -70,20 +70,20 @@ module ExpressLibraries { /** * A header produced by a route handler of the "helmet" module. */ - class HelmetRouteHandlerHeader extends HTTP::ImplicitHeaderDefinition { + class HelmetRouteHandlerHeader extends Http::ImplicitHeaderDefinition { HelmetRouteHandlerHeader() { this instanceof HelmetRouteHandler } override predicate defines(string headerName, string headerValue) { xFrameOptionsDefaultImplicitHeaderDefinition(headerName, headerValue) } - override HTTP::RouteHandler getRouteHandler() { result = this } + override Http::RouteHandler getRouteHandler() { result = this } } /** * A route handler from the "helmet" module. */ - class HelmetRouteHandler extends HTTP::RouteHandler { + class HelmetRouteHandler extends Http::RouteHandler { HelmetRouteHandler() { exists(DataFlow::ModuleImportNode m | "helmet" = m.getPath() | this = m.getAnInvocation() or @@ -91,7 +91,7 @@ module ExpressLibraries { ) } - override HTTP::HeaderDefinition getAResponseHeader(string name) { + override Http::HeaderDefinition getAResponseHeader(string name) { name = this.(HelmetRouteHandlerHeader).getAHeaderName() and result = this } @@ -108,7 +108,7 @@ module ExpressLibraries { /** * A call that creates an `express-session` middleware instance. */ - class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance { + class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance { MiddlewareInstance() { this = expressSession().getACall() } /** @@ -135,7 +135,7 @@ module ExpressLibraries { /** * A call that creates a `cookie-parser` middleware instance. */ - class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance { + class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance { MiddlewareInstance() { this = cookieParser().getACall() } /** @@ -164,7 +164,7 @@ module ExpressLibraries { /** * A call that creates a `cookie-session` middleware instance. */ - class MiddlewareInstance extends DataFlow::InvokeNode, HTTP::CookieMiddlewareInstance { + class MiddlewareInstance extends DataFlow::InvokeNode, Http::CookieMiddlewareInstance { MiddlewareInstance() { this = cookieSession().getACall() } /** diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll index 76d1c9825b3..d12c4278601 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Fastify.qll @@ -12,7 +12,7 @@ module Fastify { /** * An expression that creates a new Fastify server. */ - abstract class ServerDefinition extends HTTP::Servers::StandardServerDefinition { } + abstract class ServerDefinition extends Http::Servers::StandardServerDefinition { } /** * A standard way to create a Fastify server. @@ -76,7 +76,7 @@ module Fastify { * but support for other kinds of route handlers can be added by implementing * additional subclasses of this class. */ - abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode { + abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::ValueNode { /** * Gets the parameter of the route handler that contains the request object. */ @@ -103,7 +103,7 @@ module Fastify { * A Fastify reply source, that is, the `reply` parameter of a * route handler. */ - private class ReplySource extends HTTP::Servers::ResponseSource { + private class ReplySource extends Http::Servers::ResponseSource { RouteHandler rh; ReplySource() { this = rh.getReplyParameter() } @@ -118,7 +118,7 @@ module Fastify { * A Fastify request source, that is, the request parameter of a * route handler. */ - private class RequestSource extends HTTP::Servers::RequestSource { + private class RequestSource extends Http::Servers::RequestSource { RouteHandler rh; RequestSource() { this = rh.getRequestParameter() } @@ -132,7 +132,7 @@ module Fastify { /** * A call to a Fastify method that sets up a route. */ - class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup { + class RouteSetup extends DataFlow::MethodCallNode, Http::Servers::StandardRouteSetup { ServerDefinition server; string methodName; @@ -176,7 +176,7 @@ module Fastify { override string getRelativePath() { result = this.getArgument(0).getStringValue() } - override HTTP::RequestMethodName getHttpMethod() { result = this.getMethodName().toUpperCase() } + override Http::RequestMethodName getHttpMethod() { result = this.getMethodName().toUpperCase() } } /** Gets the name of the `n`th handler function that can be installed a route setup, in order of execution. */ @@ -194,7 +194,7 @@ module Fastify { override string getRelativePath() { result = this.getOptionArgument(0, "url").getStringValue() } - override HTTP::RequestMethodName getHttpMethod() { + override Http::RequestMethodName getHttpMethod() { result = this.getOptionArgument(0, "method").getStringValue().toUpperCase() } @@ -226,7 +226,7 @@ module Fastify { result = this.pluginBody(DataFlow::TypeBackTracker::end()) } - override HTTP::RequestMethodName getHttpMethod() { + override Http::RequestMethodName getHttpMethod() { result = this.getOptionArgument(1, "method").getStringValue().toUpperCase() } @@ -252,7 +252,7 @@ module Fastify { /** * An access to a user-controlled Fastify request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess { + private class RequestInputAccess extends Http::RequestInputAccess { RouteHandler rh; string kind; @@ -308,7 +308,7 @@ module Fastify { /** * An access to a header on a Fastify request. */ - private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess { RouteHandler rh; RequestHeaderAccess() { @@ -327,7 +327,7 @@ module Fastify { /** * An argument passed to the `send` or `end` method of an HTTP response object. */ - private class ResponseSendArgument extends HTTP::ResponseSendArgument { + private class ResponseSendArgument extends Http::ResponseSendArgument { RouteHandler rh; ResponseSendArgument() { @@ -342,7 +342,7 @@ module Fastify { /** * An invocation of the `redirect` method of an HTTP response object. */ - private class RedirectInvocation extends HTTP::RedirectInvocation, DataFlow::MethodCallNode { + private class RedirectInvocation extends Http::RedirectInvocation, DataFlow::MethodCallNode { RouteHandler rh; RedirectInvocation() { this = rh.getAResponseSource().ref().getAMethodCall("redirect") } @@ -355,7 +355,7 @@ module Fastify { /** * An invocation that sets a single header of the HTTP response. */ - private class SetOneHeader extends HTTP::Servers::StandardHeaderDefinition, + private class SetOneHeader extends Http::Servers::StandardHeaderDefinition, DataFlow::MethodCallNode { RouteHandler rh; @@ -370,7 +370,7 @@ module Fastify { /** * An invocation that sets any number of headers of the HTTP response. */ - class SetMultipleHeaders extends HTTP::ExplicitHeaderDefinition, DataFlow::MethodCallNode { + class SetMultipleHeaders extends Http::ExplicitHeaderDefinition, DataFlow::MethodCallNode { RouteHandler rh; SetMultipleHeaders() { @@ -414,7 +414,7 @@ module Fastify { override DataFlow::Node getTemplateParamsNode() { result = this.getArgument(1) } } - private class FastifyCookieMiddleware extends HTTP::CookieMiddlewareInstance { + private class FastifyCookieMiddleware extends Http::CookieMiddlewareInstance { FastifyCookieMiddleware() { this = DataFlow::moduleImport(["fastify-cookie", "fastify-session", "fastify-secure-session"]) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Firebase.qll b/javascript/ql/lib/semmle/javascript/frameworks/Firebase.qll index d22bb782630..35a8d329643 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Firebase.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Firebase.qll @@ -195,7 +195,7 @@ module Firebase { /** * A call to a Firebase method that sets up a route. */ - private class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::CallNode { + private class RouteSetup extends Http::Servers::StandardRouteSetup, DataFlow::CallNode { RouteSetup() { this = namespace().getAPropertyRead("https").getAMemberCall("onRequest") } override DataFlow::SourceNode getARouteHandler() { @@ -215,7 +215,7 @@ module Firebase { /** * A function used as a route handler. */ - private class RouteHandler extends Express::RouteHandler, HTTP::Servers::StandardRouteHandler, + private class RouteHandler extends Express::RouteHandler, Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { RouteHandler() { this = any(RouteSetup setup).getARouteHandler() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll b/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll index 626b29423c8..62788b2a3d7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/HTTP.qll @@ -8,7 +8,7 @@ private import semmle.javascript.dataflow.internal.StepSummary private import semmle.javascript.dataflow.internal.CallGraphs private import DataFlow::PseudoProperties as PseudoProperties -module HTTP { +module Http { /** * A function invocation that causes a redirect response to be sent. */ @@ -242,7 +242,7 @@ module HTTP { DataFlow::functionOneWayForwardingStep(pred.getALocalUse(), succ) or // a container containing route-handlers. - exists(HTTP::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ)) + exists(Http::RouteHandlerCandidateContainer container | pred = container.getRouteHandler(succ)) or // (function (req, res) {}).bind(this); exists(DataFlow::PartialInvokeNode call | @@ -677,7 +677,7 @@ module HTTP { /** * A collection that contains one or more route potential handlers. */ - private class ContainerCollection extends HTTP::RouteHandlerCandidateContainer::Range, + private class ContainerCollection extends Http::RouteHandlerCandidateContainer::Range, DataFlow::NewNode { ContainerCollection() { this = DataFlow::globalVarRef("Map").getAnInstantiation() and // restrict to Map for now @@ -699,3 +699,6 @@ module HTTP { } } } + +/** DEPRECATED: Alias for Http */ +deprecated module HTTP = Http; diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll index c5700f25728..e4bc922951c 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Hapi.qll @@ -9,7 +9,7 @@ module Hapi { /** * An expression that creates a new Hapi server. */ - class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::NewNode { + class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::NewNode { ServerDefinition() { // `server = new Hapi.Server()` this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation() @@ -19,7 +19,7 @@ module Hapi { /** * A Hapi route handler. */ - class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { RouteHandler() { exists(RouteSetup setup | this = setup.getARouteHandler()) } /** @@ -43,7 +43,7 @@ module Hapi { * A Hapi response source, that is, an access to the `response` property * of a request object. */ - private class ResponseSource extends HTTP::Servers::ResponseSource { + private class ResponseSource extends Http::Servers::ResponseSource { RequestNode req; ResponseSource() { this.(DataFlow::PropRead).accesses(req, "response") } @@ -58,7 +58,7 @@ module Hapi { * A Hapi request source, that is, the request parameter of a * route handler. */ - private class RequestSource extends HTTP::Servers::RequestSource { + private class RequestSource extends Http::Servers::RequestSource { RouteHandler rh; RequestSource() { this = rh.getRequestParameter() } @@ -80,7 +80,7 @@ module Hapi { /** * A Hapi response node. */ - class ResponseNode extends HTTP::Servers::StandardResponseNode { + class ResponseNode extends Http::Servers::StandardResponseNode { override ResponseSource src; } @@ -95,14 +95,14 @@ module Hapi { /** * A Hapi request node. */ - class RequestNode extends HTTP::Servers::StandardRequestNode { + class RequestNode extends Http::Servers::StandardRequestNode { override RequestSource src; } /** * An access to a user-controlled Hapi request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess { + private class RequestInputAccess extends Http::RequestInputAccess { RouteHandler rh; string kind; @@ -156,7 +156,7 @@ module Hapi { /** * An access to an HTTP header on a Hapi request. */ - private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess { RouteHandler rh; RequestHeaderAccess() { @@ -181,7 +181,7 @@ module Hapi { /** * An HTTP header defined in a Hapi server. */ - private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition { + private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { ResponseNode res; HeaderDefinition() { @@ -195,7 +195,7 @@ module Hapi { /** * A call to a Hapi method that sets up a route. */ - class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup { + class RouteSetup extends DataFlow::MethodCallNode, Http::Servers::StandardRouteSetup { ServerDefinition server; DataFlow::Node handler; @@ -236,7 +236,7 @@ module Hapi { * * For example, this could be the function `function(request, h){...}`. */ - class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate { + class RouteHandlerCandidate extends Http::RouteHandlerCandidate { RouteHandlerCandidate() { exists(string request, string responseToolkit | (request = "request" or request = "req") and @@ -256,7 +256,7 @@ module Hapi { * A function that looks like a Hapi route handler and flows to a route setup. */ private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler, - HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { TrackedRouteHandlerCandidateWithSetup() { this = any(RouteSetup s).getARouteHandler() } } @@ -276,7 +276,7 @@ module Hapi { /** * A return from a route handler. */ - private class HandlerReturn extends HTTP::ResponseSendArgument { + private class HandlerReturn extends Http::ResponseSendArgument { RouteHandler handler; HandlerReturn() { this = handler.(DataFlow::FunctionNode).getAReturn() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Koa.qll b/javascript/ql/lib/semmle/javascript/frameworks/Koa.qll index 49104de9263..e8ca5ef2ac1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Koa.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Koa.qll @@ -9,7 +9,7 @@ module Koa { /** * An expression that creates a new Koa application. */ - class AppDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::InvokeNode { + class AppDefinition extends Http::Servers::StandardServerDefinition, DataFlow::InvokeNode { AppDefinition() { // `app = new Koa()` / `app = Koa()` this = DataFlow::moduleImport("koa").getAnInvocation() @@ -19,7 +19,7 @@ module Koa { /** * An HTTP header defined in a Koa application. */ - private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition { + private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { RouteHandler rh; HeaderDefinition() { @@ -36,7 +36,7 @@ module Koa { /** * A Koa route handler. */ - abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::SourceNode { + abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::SourceNode { /** * Gets the parameter of the route handler that contains the context object. */ @@ -227,7 +227,7 @@ module Koa { * A Koa request source, that is, an access to the `request` property * of a context object. */ - private class RequestSource extends HTTP::Servers::RequestSource instanceof DataFlow::PropRead { + private class RequestSource extends Http::Servers::RequestSource instanceof DataFlow::PropRead { ContextNode ctx; RequestSource() { super.accesses(ctx, "request") } @@ -242,7 +242,7 @@ module Koa { * A Koa request source, accessed through the a request property of a * generator route handler (deprecated in Koa 3). */ - private class GeneratorRequestSource extends HTTP::Servers::RequestSource { + private class GeneratorRequestSource extends Http::Servers::RequestSource { RouteHandler rh; GeneratorRequestSource() { @@ -262,7 +262,7 @@ module Koa { * A Koa response source, that is, an access to the `response` property * of a context object. */ - private class ResponseSource extends HTTP::Servers::ResponseSource instanceof DataFlow::PropRead { + private class ResponseSource extends Http::Servers::ResponseSource instanceof DataFlow::PropRead { ContextNode ctx; ResponseSource() { super.accesses(ctx, "response") } @@ -311,7 +311,7 @@ module Koa { /** * An expression that may hold a Koa request object. */ - class RequestNode extends HTTP::Servers::StandardRequestNode { + class RequestNode extends Http::Servers::StandardRequestNode { override RequestSource src; } @@ -326,14 +326,14 @@ module Koa { /** * An expression that may hold a Koa response object. */ - class ResponseNode extends HTTP::Servers::StandardResponseNode { + class ResponseNode extends Http::Servers::StandardResponseNode { override ResponseSource src; } /** * An access to a user-controlled Koa request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess { + private class RequestInputAccess extends Http::RequestInputAccess { RouteHandler rh; string kind; @@ -399,7 +399,7 @@ module Koa { /** * An access to an HTTP header on a Koa request. */ - private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess { RouteHandler rh; RequestHeaderAccess() { @@ -435,7 +435,7 @@ module Koa { /** * A call to a Koa method that sets up a route. */ - class RouteSetup extends HTTP::Servers::StandardRouteSetup, DataFlow::MethodCallNode { + class RouteSetup extends Http::Servers::StandardRouteSetup, DataFlow::MethodCallNode { AppDefinition server; RouteSetup() { @@ -457,7 +457,7 @@ module Koa { /** * A value assigned to the body of an HTTP response object. */ - private class ResponseSendArgument extends HTTP::ResponseSendArgument { + private class ResponseSendArgument extends Http::ResponseSendArgument { RouteHandler rh; ResponseSendArgument() { @@ -470,7 +470,7 @@ module Koa { /** * An invocation of the `redirect` method of an HTTP response object. */ - private class RedirectInvocation extends HTTP::RedirectInvocation instanceof DataFlow::MethodCallNode { + private class RedirectInvocation extends Http::RedirectInvocation instanceof DataFlow::MethodCallNode { RouteHandler rh; RedirectInvocation() { super.calls(rh.getAResponseOrContextNode(), "redirect") } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll index cc90dc8d720..5a8075ed366 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/LiveServer.qll @@ -9,7 +9,7 @@ private module LiveServer { /** * An expression that imports the live-server package, seen as a server-definition. */ - class ServerDefinition extends HTTP::Servers::StandardServerDefinition { + class ServerDefinition extends Http::Servers::StandardServerDefinition { ServerDefinition() { this = DataFlow::moduleImport("live-server") } API::Node getImportNode() { result.asSource() = this } @@ -30,7 +30,7 @@ private module LiveServer { /** * The call to `require("live-server").start()`, seen as a route setup. */ - class RouteSetup extends HTTP::Servers::StandardRouteSetup instanceof API::CallNode { + class RouteSetup extends Http::Servers::StandardRouteSetup instanceof API::CallNode { ServerDefinition server; RouteSetup() { this = server.getImportNode().getMember("start").getACall() } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll index ba99fc014d1..a5599e4ee79 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Logging.qll @@ -349,7 +349,7 @@ private module Pino { or // `pino` is installed as the "log" property on the request object in `Express` and similar libraries. // in `Hapi` the property is "logger". - exists(HTTP::RequestNode req, API::Node reqNode | + exists(Http::RequestNode req, API::Node reqNode | reqNode.asSource() = req.getALocalSource() and result = reqNode.getMember(["log", "logger"]) ) diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Micro.qll b/javascript/ql/lib/semmle/javascript/frameworks/Micro.qll index 7ac9b1349d8..abdc97fe0b7 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Micro.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Micro.qll @@ -42,24 +42,24 @@ private module Micro { /** * A function passed to `micro` or `micro.run`. */ - class MicroRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + class MicroRouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { MicroRouteHandler() { this = microRouteHandler().getAFunctionValue() } } - class MicroRequestSource extends HTTP::Servers::RequestSource { + class MicroRequestSource extends Http::Servers::RequestSource { MicroRouteHandler h; MicroRequestSource() { this = h.getParameter(0) } - override HTTP::RouteHandler getRouteHandler() { result = h } + override Http::RouteHandler getRouteHandler() { result = h } } - class MicroResponseSource extends HTTP::Servers::ResponseSource { + class MicroResponseSource extends Http::Servers::ResponseSource { MicroRouteHandler h; MicroResponseSource() { this = h.getParameter(1) } - override HTTP::RouteHandler getRouteHandler() { result = h } + override Http::RouteHandler getRouteHandler() { result = h } } deprecated class MicroRequestExpr extends NodeJSLib::RequestExpr { @@ -78,19 +78,19 @@ private module Micro { override MicroResponseSource src; } - private HTTP::RouteHandler getRouteHandlerFromReqRes(DataFlow::Node node) { - exists(HTTP::Servers::RequestSource src | + private Http::RouteHandler getRouteHandlerFromReqRes(DataFlow::Node node) { + exists(Http::Servers::RequestSource src | src.ref().flowsTo(node) and result = src.getRouteHandler() ) or - exists(HTTP::Servers::ResponseSource src | + exists(Http::Servers::ResponseSource src | src.ref().flowsTo(node) and result = src.getRouteHandler() ) } - class MicroBodyParserCall extends HTTP::RequestInputAccess, DataFlow::CallNode { + class MicroBodyParserCall extends Http::RequestInputAccess, DataFlow::CallNode { string name; MicroBodyParserCall() { @@ -100,14 +100,14 @@ private module Micro { override string getKind() { result = "body" } - override HTTP::RouteHandler getRouteHandler() { + override Http::RouteHandler getRouteHandler() { result = getRouteHandlerFromReqRes(getArgument(0)) } override predicate isUserControlledObject() { name = "json" } } - class MicroSendArgument extends HTTP::ResponseSendArgument { + class MicroSendArgument extends Http::ResponseSendArgument { CallNode send; MicroSendArgument() { @@ -115,7 +115,7 @@ private module Micro { this = send.getLastArgument() } - override HTTP::RouteHandler getRouteHandler() { + override Http::RouteHandler getRouteHandler() { result = getRouteHandlerFromReqRes(send.getArgument([0, 1])) } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll index 80cba72fd3c..16a57eb1b98 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Nest.qll @@ -34,7 +34,7 @@ module NestJS { * } * ``` */ - private class NestJSRouteHandler extends HTTP::RouteHandler, DataFlow::FunctionNode { + private class NestJSRouteHandler extends Http::RouteHandler, DataFlow::FunctionNode { NestJSRouteHandler() { getAFunctionDecorator(this) = nestjs() @@ -42,7 +42,7 @@ module NestJS { .getACall() } - override HTTP::HeaderDefinition getAResponseHeader(string name) { none() } + override Http::HeaderDefinition getAResponseHeader(string name) { none() } /** * Holds if this has the `@Redirect()` decorator. @@ -257,7 +257,7 @@ module NestJS { * The type of remote flow depends on which decorator is applied at the parameter, so * we just classify it as a `RemoteFlowSource`. */ - private class NestJSCustomPipeInput extends HTTP::RequestInputAccess { + private class NestJSCustomPipeInput extends Http::RequestInputAccess { CustomPipeClass pipe; NestJSCustomPipeInput() { @@ -273,7 +273,7 @@ module NestJS { result = pipe.getAnAffectedParameter().getInputKind() } - override HTTP::RouteHandler getRouteHandler() { + override Http::RouteHandler getRouteHandler() { result = pipe.getAnAffectedParameter().getNestRouteHandler() } } @@ -295,13 +295,13 @@ module NestJS { * as a source of untrusted data. */ private class NestJSRequestInputAsRequestInputAccess extends NestJSRequestInput, - HTTP::RequestInputAccess { + Http::RequestInputAccess { NestJSRequestInputAsRequestInputAccess() { not this.isSanitizedByPipe() and not this = any(CustomPipeClass cls).getAnAffectedParameter() } - override HTTP::RouteHandler getRouteHandler() { result = this.getNestRouteHandler() } + override Http::RouteHandler getRouteHandler() { result = this.getNestRouteHandler() } override string getKind() { result = this.getInputKind() } @@ -316,7 +316,7 @@ module NestJS { } private class NestJSHeaderAccess extends NestJSRequestInputAsRequestInputAccess, - HTTP::RequestHeaderAccess { + Http::RequestHeaderAccess { NestJSHeaderAccess() { decoratorName = "Headers" and decorator.getNumArgument() > 0 } override string getAHeaderName() { @@ -344,7 +344,7 @@ module NestJS { * ``` * writes `Hello` to the response. */ - private class ReturnValueAsResponseSend extends HTTP::ResponseSendArgument { + private class ReturnValueAsResponseSend extends Http::ResponseSendArgument { NestJSRouteHandler handler; ReturnValueAsResponseSend() { @@ -357,7 +357,7 @@ module NestJS { ) } - override HTTP::RouteHandler getRouteHandler() { result = handler } + override Http::RouteHandler getRouteHandler() { result = handler } } /** @@ -439,7 +439,7 @@ module NestJS { /** * Gets the route handler that handles this request. */ - override HTTP::RouteHandler getRouteHandler() { + override Http::RouteHandler getRouteHandler() { result.(DataFlow::FunctionNode).getAParameter() = this } } @@ -456,7 +456,7 @@ module NestJS { /** * Gets the route handler that handles this request. */ - override HTTP::RouteHandler getRouteHandler() { + override Http::RouteHandler getRouteHandler() { result.(DataFlow::FunctionNode).getAParameter() = this } } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll index 495543eb1b3..50671e37796 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Next.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Next.qll @@ -153,14 +153,14 @@ module NextJS { /** * A Next.js function that is exected on the server for every request, seen as a routehandler. */ - class NextHttpRouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + class NextHttpRouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { NextHttpRouteHandler() { this = getServerSidePropsFunction(_) or this = getInitialProps(_) } } /** * A function that handles both a request and response from Next.js, seen as a routehandler. */ - class NextReqResHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + class NextReqResHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { DataFlow::ParameterNode req; DataFlow::ParameterNode res; @@ -182,28 +182,28 @@ module NextJS { * A NodeJS HTTP request object in a Next.js page. */ class NextHttpRequestSource extends NodeJSLib::RequestSource { - HTTP::RouteHandler rh; + Http::RouteHandler rh; NextHttpRequestSource() { this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("req") or this = rh.(NextReqResHandler).getRequest() } - override HTTP::RouteHandler getRouteHandler() { result = rh } + override Http::RouteHandler getRouteHandler() { result = rh } } /** * A NodeJS HTTP response object in a Next.js page. */ class NextHttpResponseSource extends NodeJSLib::ResponseSource { - HTTP::RouteHandler rh; + Http::RouteHandler rh; NextHttpResponseSource() { this = rh.(NextHttpRouteHandler).getParameter(0).getAPropertyRead("res") or this = rh.(NextReqResHandler).getResponse() } - override HTTP::RouteHandler getRouteHandler() { result = rh } + override Http::RouteHandler getRouteHandler() { result = rh } } /** @@ -222,7 +222,7 @@ module NextJS { * and we therefore model the routehandler as an Express.js routehandler. */ class NextApiRouteHandler extends DataFlow::FunctionNode, Express::RouteHandler, - HTTP::Servers::StandardRouteHandler { + Http::Servers::StandardRouteHandler { NextApiRouteHandler() { exists(Module mod | mod.getFile().getParentContainer() = apiFolder() | this = mod.getAnExportedValue("default").getAFunctionValue() diff --git a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll index 0c5fdbd6a15..36c3cb78cb9 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/NodeJSLib.qll @@ -81,7 +81,7 @@ module NodeJSLib { * A server library that provides an (enhanced) NodesJS HTTP response * object should implement a library specific subclass of this class. */ - abstract class ResponseNode extends HTTP::Servers::StandardResponseNode { } + abstract class ResponseNode extends Http::Servers::StandardResponseNode { } /** * DEPRECATED: Use `RequestNode` instead. @@ -100,7 +100,7 @@ module NodeJSLib { * A server library that provides an (enhanced) NodesJS HTTP request * object should implement a library specific subclass of this class. */ - abstract class RequestNode extends HTTP::Servers::StandardRequestNode { } + abstract class RequestNode extends Http::Servers::StandardRequestNode { } /** * A function used as an Node.js server route handler. @@ -109,7 +109,7 @@ module NodeJSLib { * but support for other kinds of route handlers can be added by implementing * additional subclasses of this class. */ - abstract class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + abstract class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { /** * Gets the parameter of the route handler that contains the request object. */ @@ -131,7 +131,7 @@ module NodeJSLib { /** * A Node.js response source. */ - abstract class ResponseSource extends HTTP::Servers::ResponseSource { } + abstract class ResponseSource extends Http::Servers::ResponseSource { } /** * A standard Node.js response source, that is, the response parameter of a @@ -151,7 +151,7 @@ module NodeJSLib { /** * A Node.js request source. */ - abstract class RequestSource extends HTTP::Servers::RequestSource { } + abstract class RequestSource extends Http::Servers::RequestSource { } /** * A standard Node.js request source, that is, the request parameter of a @@ -201,7 +201,7 @@ module NodeJSLib { /** * An access to a user-controlled Node.js request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess { + private class RequestInputAccess extends Http::RequestInputAccess { RequestNode request; string kind; @@ -223,7 +223,7 @@ module NodeJSLib { ) } - override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() } + override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() } override string getKind() { result = kind } } @@ -231,7 +231,7 @@ module NodeJSLib { /** * An access to an HTTP header (other than "Cookie") on an incoming Node.js request object. */ - private class RequestHeaderAccess extends HTTP::RequestHeaderAccess { + private class RequestHeaderAccess extends Http::RequestHeaderAccess { RequestNode request; RequestHeaderAccess() { @@ -247,14 +247,14 @@ module NodeJSLib { result = this.(DataFlow::PropRead).getPropertyName().toLowerCase() } - override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() } + override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() } override string getKind() { result = "header" } RequestNode getRequest() { result = request } } - class RouteSetup extends DataFlow::CallNode, HTTP::Servers::StandardRouteSetup { + class RouteSetup extends DataFlow::CallNode, Http::Servers::StandardRouteSetup { ServerDefinition server; DataFlow::Node handler; @@ -282,7 +282,7 @@ module NodeJSLib { result = succ.backtrack(t2, t) or t = t2 and - HTTP::routeHandlerStep(result, succ) + Http::routeHandlerStep(result, succ) ) } @@ -300,12 +300,12 @@ module NodeJSLib { DataFlow::Node getRouteHandlerNode() { result = handler } } - abstract private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition { + abstract private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { ResponseNode r; HeaderDefinition() { this.getReceiver() = r } - override HTTP::RouteHandler getRouteHandler() { result = r.getRouteHandler() } + override Http::RouteHandler getRouteHandler() { result = r.getRouteHandler() } } /** @@ -403,8 +403,8 @@ module NodeJSLib { * An expression passed as the first argument to the `write` or `end` method * of an HTTP response. */ - private class ResponseSendArgument extends HTTP::ResponseSendArgument { - HTTP::RouteHandler rh; + private class ResponseSendArgument extends Http::ResponseSendArgument { + Http::RouteHandler rh; ResponseSendArgument() { exists(DataFlow::MethodCallNode mcn, string m | m = "write" or m = "end" | @@ -415,13 +415,13 @@ module NodeJSLib { ) } - override HTTP::RouteHandler getRouteHandler() { result = rh } + override Http::RouteHandler getRouteHandler() { result = rh } } /** * An expression that creates a new Node.js server. */ - class ServerDefinition extends HTTP::Servers::StandardServerDefinition { + class ServerDefinition extends Http::Servers::StandardServerDefinition { ServerDefinition() { isCreateServer(this) } } @@ -820,7 +820,7 @@ module NodeJSLib { * * For example, this could be the function `function(req, res){...}`. */ - class RouteHandlerCandidate extends HTTP::RouteHandlerCandidate { + class RouteHandlerCandidate extends Http::RouteHandlerCandidate { RouteHandlerCandidate() { exists(string request, string response | (request = "request" or request = "req") and @@ -840,7 +840,7 @@ module NodeJSLib { * A function that flows to a route setup. */ private class TrackedRouteHandlerCandidateWithSetup extends RouteHandler, - HTTP::Servers::StandardRouteHandler, DataFlow::FunctionNode { + Http::Servers::StandardRouteHandler, DataFlow::FunctionNode { TrackedRouteHandlerCandidateWithSetup() { this = any(RouteSetup s).getARouteHandler() } } @@ -871,7 +871,7 @@ module NodeJSLib { * For example, this could be the call `server.on("request", handler)` * where it is unknown if `server` is a Node.js server. */ - class RouteSetupCandidate extends HTTP::RouteSetupCandidate, DataFlow::MethodCallNode { + class RouteSetupCandidate extends Http::RouteSetupCandidate, DataFlow::MethodCallNode { DataFlow::ValueNode arg; RouteSetupCandidate() { @@ -912,7 +912,7 @@ module NodeJSLib { exists(string moduleName, DataFlow::SourceNode callee | this = callee.getACall() | (moduleName = "http" or moduleName = "https") and ( - callee = DataFlow::moduleMember(moduleName, any(HTTP::RequestMethodName m).toLowerCase()) + callee = DataFlow::moduleMember(moduleName, any(Http::RequestMethodName m).toLowerCase()) or callee = DataFlow::moduleMember(moduleName, "request") ) and diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Request.qll b/javascript/ql/lib/semmle/javascript/frameworks/Request.qll index 392a84c51a4..3551da6f0c1 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Request.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Request.qll @@ -17,7 +17,7 @@ module Request { action = mod.getAnInvocation() or // specialized form: `request.get(...)` - action = mod.getAMemberCall(any(HTTP::RequestMethodName n).toLowerCase()) + action = mod.getAMemberCall(any(Http::RequestMethodName n).toLowerCase()) ) | exists(DataFlow::MethodCallNode auth, int argIndex | diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll index 492416171da..889369c6ea4 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Restify.qll @@ -9,7 +9,7 @@ module Restify { /** * An expression that creates a new Restify server. */ - class ServerDefinition extends HTTP::Servers::StandardServerDefinition, DataFlow::CallNode { + class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::CallNode { ServerDefinition() { // `server = restify.createServer()` this = DataFlow::moduleMember("restify", "createServer").getACall() @@ -19,7 +19,7 @@ module Restify { /** * A Restify route handler. */ - class RouteHandler extends HTTP::Servers::StandardRouteHandler, DataFlow::ValueNode { + class RouteHandler extends Http::Servers::StandardRouteHandler, DataFlow::ValueNode { Function function; RouteHandler() { @@ -42,7 +42,7 @@ module Restify { * A Restify response source, that is, the response parameter of a * route handler. */ - private class ResponseSource extends HTTP::Servers::ResponseSource { + private class ResponseSource extends Http::Servers::ResponseSource { RouteHandler rh; ResponseSource() { this = DataFlow::parameterNode(rh.getResponseParameter()) } @@ -57,7 +57,7 @@ module Restify { * A Restify request source, that is, the request parameter of a * route handler. */ - private class RequestSource extends HTTP::Servers::RequestSource { + private class RequestSource extends Http::Servers::RequestSource { RouteHandler rh; RequestSource() { this = DataFlow::parameterNode(rh.getRequestParameter()) } @@ -101,7 +101,7 @@ module Restify { /** * An access to a user-controlled Restify request input. */ - private class RequestInputAccess extends HTTP::RequestInputAccess { + private class RequestInputAccess extends Http::RequestInputAccess { RequestNode request; string kind; @@ -140,7 +140,7 @@ module Restify { /** * An HTTP header defined in a Restify server. */ - private class HeaderDefinition extends HTTP::Servers::StandardHeaderDefinition { + private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition { HeaderDefinition() { // response.header('Cache-Control', 'no-cache') this.getReceiver() instanceof ResponseNode and @@ -153,13 +153,13 @@ module Restify { /** * A call to a Restify method that sets up a route. */ - class RouteSetup extends DataFlow::MethodCallNode, HTTP::Servers::StandardRouteSetup { + class RouteSetup extends DataFlow::MethodCallNode, Http::Servers::StandardRouteSetup { ServerDefinition server; RouteSetup() { // server.get('/', fun) // server.head('/', fun) - server.ref().getAMethodCall(any(HTTP::RequestMethodName m).toLowerCase()) = this + server.ref().getAMethodCall(any(Http::RequestMethodName m).toLowerCase()) = this } override DataFlow::SourceNode getARouteHandler() { result.flowsTo(this.getArgument(1)) } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll index 90adf7d7de6..d66ca3baab2 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Templating.qll @@ -151,7 +151,7 @@ module Templating { /** Gets the data flow node representing the initialization of the given variable in this scope. */ DataFlow::Node getVariableInit(string name) { - result = DataFlow::ssaDefinitionNode(SSA::implicitInit(this.getScope().getVariable(name))) + result = DataFlow::ssaDefinitionNode(Ssa::implicitInit(this.getScope().getVariable(name))) } /** Gets a data flow node corresponding to a use of the given template variable within this top-level. */ diff --git a/javascript/ql/lib/semmle/javascript/frameworks/WebSocket.qll b/javascript/ql/lib/semmle/javascript/frameworks/WebSocket.qll index 16e5187081e..a02da6c9f86 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/WebSocket.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/WebSocket.qll @@ -226,21 +226,21 @@ module ServerWebSocket { * A `socket.on("connection", (msg, req) => {})` call seen as a HTTP route handler. * `req` is a `HTTP::IncomingMessage` instance. */ - class ConnectionCallAsRouteHandler extends HTTP::RouteHandler, DataFlow::CallNode { + class ConnectionCallAsRouteHandler extends Http::RouteHandler, DataFlow::CallNode { ConnectionCallAsRouteHandler() { this = getAConnectionCall(_) } - override HTTP::HeaderDefinition getAResponseHeader(string name) { none() } + override Http::HeaderDefinition getAResponseHeader(string name) { none() } } /** * The `req` parameter of a `socket.on("connection", (msg, req) => {})` call. */ - class ServerHttpRequest extends HTTP::Servers::RequestSource { + class ServerHttpRequest extends Http::Servers::RequestSource { ConnectionCallAsRouteHandler handler; ServerHttpRequest() { this = handler.getCallback(1).getParameter(1) } - override HTTP::RouteHandler getRouteHandler() { result = handler } + override Http::RouteHandler getRouteHandler() { result = handler } } /** DEPRECATED: Alias for ServerHttpRequest */ @@ -249,7 +249,7 @@ module ServerWebSocket { /** * An access user-controlled HTTP request input in a request to a WebSocket server. */ - class WebSocketRequestInput extends HTTP::RequestInputAccess { + class WebSocketRequestInput extends Http::RequestInputAccess { ServerHttpRequest request; string kind; @@ -267,7 +267,7 @@ module ServerWebSocket { override string getKind() { result = kind } - override HTTP::RouteHandler getRouteHandler() { result = request.getRouteHandler() } + override Http::RouteHandler getRouteHandler() { result = request.getRouteHandler() } } /** diff --git a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll index 1d873a3f2a9..76a8aff557b 100644 --- a/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll +++ b/javascript/ql/lib/semmle/javascript/heuristics/AdditionalRouteHandlers.qll @@ -11,14 +11,14 @@ private import semmle.javascript.frameworks.ConnectExpressShared * Add `NodeJSLib::RouteHandlerCandidate` to the extent of `NodeJSLib::RouteHandler`. */ private class PromotedNodeJSLibCandidate extends NodeJSLib::RouteHandler, - HTTP::Servers::StandardRouteHandler { + Http::Servers::StandardRouteHandler { PromotedNodeJSLibCandidate() { this instanceof NodeJSLib::RouteHandlerCandidate } } /** * Add `Hapi::RouteHandlerCandidate` to the extent of `Hapi::RouteHandler`. */ -private class PromotedHapiCandidate extends Hapi::RouteHandler, HTTP::Servers::StandardRouteHandler { +private class PromotedHapiCandidate extends Hapi::RouteHandler, Http::Servers::StandardRouteHandler { PromotedHapiCandidate() { this instanceof Hapi::RouteHandlerCandidate } } @@ -26,7 +26,7 @@ private class PromotedHapiCandidate extends Hapi::RouteHandler, HTTP::Servers::S * Add `ConnectExpressShared::RouteHandlerCandidate` to the extent of `Express::RouteHandler`. */ private class PromotedExpressCandidate extends Express::RouteHandler, - HTTP::Servers::StandardRouteHandler { + Http::Servers::StandardRouteHandler { PromotedExpressCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate } override DataFlow::ParameterNode getRouteHandlerParameter(string kind) { @@ -38,7 +38,7 @@ private class PromotedExpressCandidate extends Express::RouteHandler, * Add `ConnectExpressShared::RouteHandlerCandidate` to the extent of `Connect::RouteHandler`. */ private class PromotedConnectCandidate extends Connect::RouteHandler, - HTTP::Servers::StandardRouteHandler { + Http::Servers::StandardRouteHandler { PromotedConnectCandidate() { this instanceof ConnectExpressShared::RouteHandlerCandidate } override DataFlow::ParameterNode getRouteHandlerParameter(string kind) { diff --git a/javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll b/javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll index e11bc5182f0..65e662f0bc5 100644 --- a/javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/OverlyLargeRangeQuery.qll @@ -96,7 +96,10 @@ class OverlyWideRange extends RegExpCharacterRange { toCodePoint("A") <= high or // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) + exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and + // while still being ascii + low < 128 and + high < 128 ) and // allowlist for known ranges not this = allowedWideRanges() diff --git a/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll b/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll index 504ebad9e35..aad1e3fddd6 100644 --- a/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll +++ b/javascript/ql/lib/semmle/javascript/security/SensitiveActions.qll @@ -96,7 +96,7 @@ private predicate writesProperty(DataFlow::Node node, string name) { exists(VarDef v | v.getAVariable().getName() = name | if exists(v.getSource()) then v.getSource() = node.asExpr() - else node = DataFlow::ssaDefinitionNode(SSA::definition(v)) + else node = DataFlow::ssaDefinitionNode(Ssa::definition(v)) ) } diff --git a/javascript/ql/lib/semmle/javascript/security/TaintedObject.qll b/javascript/ql/lib/semmle/javascript/security/TaintedObject.qll index 6c5c0f21057..eac7dd4e762 100644 --- a/javascript/ql/lib/semmle/javascript/security/TaintedObject.qll +++ b/javascript/ql/lib/semmle/javascript/security/TaintedObject.qll @@ -75,7 +75,7 @@ module TaintedObject { /** Request input accesses as a JSON source. */ private class RequestInputAsSource extends Source { - RequestInputAsSource() { this.(HTTP::RequestInputAccess).isUserControlledObject() } + RequestInputAsSource() { this.(Http::RequestInputAccess).isUserControlledObject() } } /** diff --git a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll index c2244de9d7e..5428e14abee 100644 --- a/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll +++ b/javascript/ql/lib/semmle/javascript/security/UselessUseOfCat.qll @@ -127,7 +127,7 @@ class UselessCat extends CommandCall { or // `exec` can use 3 parameters, `readFile` can only use two, so it is OK to have a third parameter if it is unused, func.getNumParameter() = 3 and - not exists(SSA::definition(func.getParameter(2).getParameter())) + not exists(Ssa::definition(func.getParameter(2).getParameter())) ) ) and // The process returned by an async call is unused. diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll index ef2090b3dea..d158e4b0cac 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CleartextStorageCustomizations.qll @@ -49,7 +49,7 @@ module CleartextStorage { */ class CookieStorageSink extends Sink { CookieStorageSink() { - exists(HTTP::CookieDefinition cookieDef | + exists(Http::CookieDefinition cookieDef | this = cookieDef.getValueArgument() or this = cookieDef.getHeaderArgument() ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll index 24c9a707bd0..79868ea9989 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll @@ -19,7 +19,7 @@ module CorsMisconfigurationForCredentials { /** * Gets the "Access-Control-Allow-Credentials" header definition. */ - abstract HTTP::HeaderDefinition getCredentialsHeader(); + abstract Http::HeaderDefinition getCredentialsHeader(); } /** @@ -41,11 +41,11 @@ module CorsMisconfigurationForCredentials { * HTTP header with a truthy value. */ class CorsOriginHeaderWithAssociatedCredentialHeader extends Sink, DataFlow::ValueNode { - HTTP::ExplicitHeaderDefinition credentials; + Http::ExplicitHeaderDefinition credentials; CorsOriginHeaderWithAssociatedCredentialHeader() { exists( - HTTP::RouteHandler routeHandler, HTTP::ExplicitHeaderDefinition origin, + Http::RouteHandler routeHandler, Http::ExplicitHeaderDefinition origin, DataFlow::Node credentialsValue | routeHandler.getAResponseHeader(_) = origin and @@ -58,7 +58,7 @@ module CorsMisconfigurationForCredentials { ) } - override HTTP::HeaderDefinition getCredentialsHeader() { result = credentials } + override Http::HeaderDefinition getCredentialsHeader() { result = credentials } } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DifferentKindsComparisonBypassCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DifferentKindsComparisonBypassCustomizations.qll index bfb30cc2c99..956fe352854 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DifferentKindsComparisonBypassCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DifferentKindsComparisonBypassCustomizations.qll @@ -31,7 +31,7 @@ module DifferentKindsComparisonBypass { * A HTTP request input that is suspicious to compare with another HTTP request input of a different kind. */ class RequestInputComparisonSource extends Source { - HTTP::RequestInputAccess input; + Http::RequestInputAccess input; RequestInputComparisonSource() { input = this } @@ -42,7 +42,7 @@ module DifferentKindsComparisonBypass { /** * Gets the HTTP request input of this source. */ - private HTTP::RequestInputAccess getInput() { result = input } + private Http::RequestInputAccess getInput() { result = input } } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssQuery.qll index 72c4020526b..e8c1a144920 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/DomBasedXssQuery.qll @@ -75,7 +75,7 @@ class Configuration extends TaintTracking::Configuration { } override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { - guard instanceof PrefixStringSanitizer or + guard instanceof PrefixStringSanitizerActivated or guard instanceof QuoteGuard or guard instanceof ContainsHtmlGuard } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/HostHeaderPoisoningInEmailGenerationQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/HostHeaderPoisoningInEmailGenerationQuery.qll index 2a41b76dc80..f87938dfb71 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/HostHeaderPoisoningInEmailGenerationQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/HostHeaderPoisoningInEmailGenerationQuery.qll @@ -12,7 +12,7 @@ class Configuration extends TaintTracking::Configuration { Configuration() { this = "TaintedHostHeader" } override predicate isSource(DataFlow::Node node) { - exists(HTTP::RequestHeaderAccess input | node = input | + exists(Http::RequestHeaderAccess input | node = input | input.getKind() = "header" and input.getAHeaderName() = "host" ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessSpecific.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessSpecific.qll index a7b6cca1360..7ed38fdf3f2 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessSpecific.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/HttpToFileAccessSpecific.qll @@ -9,7 +9,7 @@ private import HttpToFileAccessCustomizations::HttpToFileAccess * An access to a user-controlled HTTP request input, considered as a flow source for writing user-controlled data to files */ private class RequestInputAccessAsSource extends Source { - RequestInputAccessAsSource() { this instanceof HTTP::RequestInputAccess } + RequestInputAccessAsSource() { this instanceof Http::RequestInputAccess } } /** A response from a server, considered as a flow source for writing user-controlled data to files. */ diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll index 23abee9db2d..536c5ebf971 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/PrototypePollutingAssignmentQuery.qll @@ -124,7 +124,8 @@ class Configuration extends TaintTracking::Configuration { guard instanceof TypeofCheck or guard instanceof NumberGuard or guard instanceof EqualityCheck or - guard instanceof IncludesCheck + guard instanceof IncludesCheck or + guard instanceof DenyListInclusionGuard } } @@ -275,3 +276,21 @@ private class IncludesCheck extends TaintTracking::LabeledSanitizerGuardNode, In outcome = this.getPolarity().booleanNot() } } + +/** + * A sanitizer guard that checks tests whether `x` is included in a list like `["__proto__"].includes(x)`. + */ +private class DenyListInclusionGuard extends TaintTracking::SanitizerGuardNode, InclusionTest { + DenyListInclusionGuard() { + this.getContainerNode() + .getALocalSource() + .(DataFlow::ArrayCreationNode) + .getAnElement() + .mayHaveStringValue("__proto__") + } + + override predicate sanitizes(boolean outcome, Expr e) { + e = this.getContainedNode().asExpr() and + outcome = super.getPolarity().booleanNot() + } +} diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ReflectedXssCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ReflectedXssCustomizations.qll index 2969d118c60..2b8f9112dd2 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ReflectedXssCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ReflectedXssCustomizations.qll @@ -24,15 +24,15 @@ module ReflectedXss { * a content type that does not (case-insensitively) contain the string "html". This * is to prevent us from flagging plain-text or JSON responses as vulnerable. */ - class HttpResponseSink extends Sink instanceof HTTP::ResponseSendArgument { + class HttpResponseSink extends Sink instanceof Http::ResponseSendArgument { HttpResponseSink() { not exists(getANonHtmlHeaderDefinition(this)) } } /** * Gets a HeaderDefinition that defines a non-html content-type for `send`. */ - HTTP::HeaderDefinition getANonHtmlHeaderDefinition(HTTP::ResponseSendArgument send) { - exists(HTTP::RouteHandler h | + Http::HeaderDefinition getANonHtmlHeaderDefinition(Http::ResponseSendArgument send) { + exists(Http::RouteHandler h | send.getRouteHandler() = h and result = nonHtmlContentTypeHeader(h) | @@ -44,7 +44,7 @@ module ReflectedXss { /** * Holds if `h` may send a response with a content type other than HTML. */ - HTTP::HeaderDefinition nonHtmlContentTypeHeader(HTTP::RouteHandler h) { + Http::HeaderDefinition nonHtmlContentTypeHeader(Http::RouteHandler h) { result = h.getAResponseHeader("content-type") and not exists(string tp | result.defines("content-type", tp) | tp.regexpMatch("(?i).*html.*")) } @@ -52,7 +52,7 @@ module ReflectedXss { /** * Holds if a header set in `header` is likely to affect a response sent at `sender`. */ - predicate headerAffects(HTTP::HeaderDefinition header, HTTP::ResponseSendArgument sender) { + predicate headerAffects(Http::HeaderDefinition header, Http::ResponseSendArgument sender) { sender.getRouteHandler() = header.getRouteHandler() and ( // `sender` is affected by a dominating `header`. @@ -60,7 +60,7 @@ module ReflectedXss { or // There is no dominating header, and `header` is non-local. not isLocalHeaderDefinition(header) and - not exists(HTTP::HeaderDefinition dominatingHeader | + not exists(Http::HeaderDefinition dominatingHeader | dominatingHeader.getBasicBlock().(ReachableBasicBlock).dominates(sender.getBasicBlock()) ) ) @@ -77,10 +77,10 @@ module ReflectedXss { * return; * ``` */ - predicate isLocalHeaderDefinition(HTTP::HeaderDefinition header) { + predicate isLocalHeaderDefinition(Http::HeaderDefinition header) { exists(ReachableBasicBlock headerBlock | headerBlock = header.getBasicBlock() | 1 = - strictcount(HTTP::ResponseSendArgument sender | + strictcount(Http::ResponseSendArgument sender | sender.getRouteHandler() = header.getRouteHandler() and header.getBasicBlock().(ReachableBasicBlock).dominates(sender.getBasicBlock()) ) and @@ -108,9 +108,9 @@ module ReflectedXss { /** A third-party controllable request input, considered as a flow source for reflected XSS. */ class ThirdPartyRequestInputAccessAsSource extends Source { ThirdPartyRequestInputAccessAsSource() { - this.(HTTP::RequestInputAccess).isThirdPartyControllable() + this.(Http::RequestInputAccess).isThirdPartyControllable() or - this.(HTTP::RequestHeaderAccess).getAHeaderName() = "referer" + this.(Http::RequestHeaderAccess).getAHeaderName() = "referer" } } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/RemotePropertyInjectionCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/RemotePropertyInjectionCustomizations.qll index d060f91b371..c4605f655d8 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/RemotePropertyInjectionCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/RemotePropertyInjectionCustomizations.qll @@ -59,7 +59,7 @@ module RemotePropertyInjection { */ class HeaderNameSink extends Sink { HeaderNameSink() { - exists(HTTP::ExplicitHeaderDefinition hd | + exists(Http::ExplicitHeaderDefinition hd | not hd instanceof Express::SetMultipleHeaders and this = hd.getNameNode() ) diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll index db62f29c8dd..c15dbc2ecf8 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/ServerSideUrlRedirectCustomizations.qll @@ -26,7 +26,7 @@ module ServerSideUrlRedirect { /** A source of third-party user input, considered as a flow source for URL redirects. */ class ThirdPartyRequestInputAccessAsSource extends Source { ThirdPartyRequestInputAccessAsSource() { - this.(HTTP::RequestInputAccess).isThirdPartyControllable() + this.(Http::RequestInputAccess).isThirdPartyControllable() } } @@ -34,7 +34,7 @@ module ServerSideUrlRedirect { * An HTTP redirect, considered as a sink for `Configuration`. */ class RedirectSink extends Sink { - RedirectSink() { this = any(HTTP::RedirectInvocation redir).getUrlArgument() } + RedirectSink() { this = any(Http::RedirectInvocation redir).getUrlArgument() } } /** @@ -43,7 +43,7 @@ module ServerSideUrlRedirect { */ class LocationHeaderSink extends Sink { LocationHeaderSink() { - any(HTTP::ExplicitHeaderDefinition def).definesHeaderValue("location", this) + any(Http::ExplicitHeaderDefinition def).definesHeaderValue("location", this) } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/StackTraceExposureCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/StackTraceExposureCustomizations.qll index 1ee969205fd..205c00f8f79 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/StackTraceExposureCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/StackTraceExposureCustomizations.qll @@ -32,5 +32,5 @@ module StackTraceExposure { * An expression that can become part of an HTTP response body, viewed * as a data flow sink for stack trace exposure vulnerabilities. */ - class DefaultSink extends Sink instanceof HTTP::ResponseBody { } + class DefaultSink extends Sink instanceof Http::ResponseBody { } } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll index 3cdc81c66ee..ad608017115 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/TypeConfusionThroughParameterTamperingCustomizations.qll @@ -29,7 +29,7 @@ module TypeConfusionThroughParameterTampering { * Node.js-based HTTP servers turn request parameters into arrays if their names are repeated. */ private class TypeTamperableRequestParameter extends Source { - TypeTamperableRequestParameter() { this.(HTTP::RequestInputAccess).isUserControlledObject() } + TypeTamperableRequestParameter() { this.(Http::RequestInputAccess).isUserControlledObject() } } /** diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionQuery.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionQuery.qll index bf8468572b4..8e5060036af 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionQuery.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/UnsafeHtmlConstructionQuery.qll @@ -43,7 +43,8 @@ class Configration extends TaintTracking::Configuration { override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) { guard instanceof PrefixStringSanitizer or guard instanceof QuoteGuard or - guard instanceof ContainsHtmlGuard + guard instanceof ContainsHtmlGuard or + guard instanceof TypeTestGuard } } diff --git a/javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll b/javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll index 033b8aa8cfd..5112bdad11e 100644 --- a/javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll +++ b/javascript/ql/lib/semmle/javascript/security/regexp/NfaUtils.qll @@ -887,11 +887,10 @@ module PrefixConstruction { /** * Holds if `state` is the textually last start state for the regular expression. */ - private predicate lastStartState(State state) { + private predicate lastStartState(RelevantState state) { exists(RegExpRoot root | state = - max(State s, Location l | - s = stateInRelevantRegexp() and + max(RelevantState s, Location l | isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() @@ -963,10 +962,17 @@ module PrefixConstruction { min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) } - /** Gets a state within a regular expression that contains a candidate state. */ - pragma[noinline] - State stateInRelevantRegexp() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(result.getRepr())) + /** A state within a regular expression that contains a candidate state. */ + class RelevantState instanceof State { + RelevantState() { + exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + + /** Gets a string representation for this state in a regular expression. */ + string toString() { result = State.super.toString() } + + /** Gets the term represented by this state. */ + RegExpTerm getRepr() { result = State.super.getRepr() } } } @@ -1007,6 +1013,8 @@ module ReDoSPruning { import PrefixConstruction as Prefix + class RelevantState = Prefix::RelevantState; + /** * Predicates for testing the presence of a rejecting suffix. * @@ -1040,32 +1048,26 @@ module ReDoSPruning { * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. */ pragma[noinline] - private predicate isLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and - ( - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - ) + private predicate isLikelyRejectable(RelevantState s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) } /** * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. */ - predicate isRejectState(State s) { - s = Prefix::stateInRelevantRegexp() and not epsilonSucc*(s) = Accept(_) - } + predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } /** * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. */ pragma[noopt] - predicate hasEdgeToLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and + predicate hasEdgeToLikelyRejectable(RelevantState s) { // all edges (at least one) with some char leads to another state that is rejectable. // the `next` states might not share a common suffix, which can cause FPs. exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | @@ -1080,8 +1082,7 @@ module ReDoSPruning { * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. */ pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(State s) { - s = Prefix::stateInRelevantRegexp() and + private string hasEdgeToLikelyRejectableHelper(RelevantState s) { not hasRejectEdge(s) and not isRejectState(s) and deltaClosedChar(s, result, _) @@ -1092,9 +1093,7 @@ module ReDoSPruning { * along epsilon edges, such that there is a transition from * `prev` to `next` that the character symbol `char`. */ - predicate deltaClosedChar(State prev, string char, State next) { - prev = Prefix::stateInRelevantRegexp() and - next = Prefix::stateInRelevantRegexp() and + predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) } diff --git a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll index ad464f8f24c..87f9437196f 100644 --- a/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/regexp/PolynomialReDoSCustomizations.qll @@ -48,8 +48,8 @@ module PolynomialReDoS { * A remote input to a server, seen as a source for polynomial * regular expression denial-of-service vulnerabilities. */ - class RequestInputAccessAsSource extends Source instanceof HTTP::RequestInputAccess { - override string getKind() { result = HTTP::RequestInputAccess.super.getKind() } + class RequestInputAccessAsSource extends Source instanceof Http::RequestInputAccess { + override string getKind() { result = Http::RequestInputAccess.super.getKind() } } /** diff --git a/javascript/ql/src/Expressions/Misspelling.qll b/javascript/ql/src/Expressions/Misspelling.qll index e7c35260edb..966e3d5e996 100644 --- a/javascript/ql/src/Expressions/Misspelling.qll +++ b/javascript/ql/src/Expressions/Misspelling.qll @@ -4,7 +4,7 @@ import javascript // import typo database (generated from Wikipedia, licensed under CC BY-SA 3.0) -import TypoDatabase +import codeql.typos.TypoDatabase /** * Holds if `wrong` is a misspelling of `right` that might be intentional or diff --git a/javascript/ql/src/Expressions/TypoDatabase.qll b/javascript/ql/src/Expressions/TypoDatabase.qll index a41f003a8c0..f480b9ebc8b 100644 --- a/javascript/ql/src/Expressions/TypoDatabase.qll +++ b/javascript/ql/src/Expressions/TypoDatabase.qll @@ -1,9035 +1,4 @@ -/** - * Holds if `wrong` is a common misspelling of `right`. - * - * This predicate was automatically generated by - * python buildutils-internal/scripts/generate-typos.py - * which uses http://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines (with custom additions) on 2018-01-19. - * - * This file is available under the Creative Commons Attribution-ShareAlike License - * (https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License). - */ -predicate typos(string wrong, string right) { - wrong = "abandonned" and right = "abandoned" - or - wrong = "abbout" and right = "about" - or - wrong = "aberation" and right = "aberration" - or - wrong = "abilityes" and right = "abilities" - or - wrong = "abilties" and right = "abilities" - or - wrong = "abilty" and right = "ability" - or - wrong = "abondon" and right = "abandon" - or - wrong = "abondoned" and right = "abandoned" - or - wrong = "abondoning" and right = "abandoning" - or - wrong = "abondons" and right = "abandons" - or - wrong = "aborigene" and right = "aborigine" - or - wrong = "abortificant" and right = "abortifacient" - or - wrong = "abotu" and right = "about" - or - wrong = "abreviate" and right = "abbreviate" - or - wrong = "abreviated" and right = "abbreviated" - or - wrong = "abreviation" and right = "abbreviation" - or - wrong = "abritrary" and right = "arbitrary" - or - wrong = "absail" and right = "abseil" - or - wrong = "absailing" and right = "abseiling" - or - wrong = "abscence" and right = "absence" - or - wrong = "absense" and right = "absence" - or - wrong = "absolutly" and right = "absolutely" - or - wrong = "absorbsion" and right = "absorption" - or - wrong = "absorbtion" and right = "absorption" - or - wrong = "abudance" and right = "abundance" - or - wrong = "abundacies" and right = "abundances" - or - wrong = "abundancies" and right = "abundances" - or - wrong = "abundunt" and right = "abundant" - or - wrong = "abutts" and right = "abuts" - or - wrong = "acadamy" and right = "academy" - or - wrong = "acadmic" and right = "academic" - or - wrong = "accademic" and right = "academic" - or - wrong = "accademy" and right = "academy" - or - wrong = "acccess" and right = "access" - or - wrong = "acccused" and right = "accused" - or - wrong = "accelleration" and right = "acceleration" - or - wrong = "accension" and right = "accession" - or - wrong = "accension" and right = "ascension" - or - wrong = "acceptence" and right = "acceptance" - or - wrong = "acceptible" and right = "acceptable" - or - wrong = "accesories" and right = "accessories" - or - wrong = "accessable" and right = "accessible" - or - wrong = "accidant" and right = "accident" - or - wrong = "accidentaly" and right = "accidentally" - or - wrong = "accidently" and right = "accidentally" - or - wrong = "acclimitization" and right = "acclimatization" - or - wrong = "accomadate" and right = "accommodate" - or - wrong = "accomadated" and right = "accommodated" - or - wrong = "accomadates" and right = "accommodates" - or - wrong = "accomadating" and right = "accommodating" - or - wrong = "accomadation" and right = "accommodation" - or - wrong = "accomadations" and right = "accommodations" - or - wrong = "accomdate" and right = "accommodate" - or - wrong = "accomodate" and right = "accommodate" - or - wrong = "accomodated" and right = "accommodated" - or - wrong = "accomodates" and right = "accommodates" - or - wrong = "accomodating" and right = "accommodating" - or - wrong = "accomodation" and right = "accommodation" - or - wrong = "accomodations" and right = "accommodations" - or - wrong = "accompanyed" and right = "accompanied" - or - wrong = "accordeon" and right = "accordion" - or - wrong = "accordian" and right = "accordion" - or - wrong = "accoring" and right = "according" - or - wrong = "accoustic" and right = "acoustic" - or - wrong = "accquainted" and right = "acquainted" - or - wrong = "accrediation" and right = "accreditation" - or - wrong = "accredidation" and right = "accreditation" - or - wrong = "accross" and right = "across" - or - wrong = "accussed" and right = "accused" - or - wrong = "acedemic" and right = "academic" - or - wrong = "acheive" and right = "achieve" - or - wrong = "acheived" and right = "achieved" - or - wrong = "acheivement" and right = "achievement" - or - wrong = "acheivements" and right = "achievements" - or - wrong = "acheives" and right = "achieves" - or - wrong = "acheiving" and right = "achieving" - or - wrong = "acheivment" and right = "achievement" - or - wrong = "acheivments" and right = "achievements" - or - wrong = "achievment" and right = "achievement" - or - wrong = "achievments" and right = "achievements" - or - wrong = "achive" and right = "achieve" - or - wrong = "achive" and right = "archive" - or - wrong = "achived" and right = "achieved" - or - wrong = "achived" and right = "archived" - or - wrong = "achivement" and right = "achievement" - or - wrong = "achivements" and right = "achievements" - or - wrong = "acident" and right = "accident" - or - wrong = "acknowldeged" and right = "acknowledged" - or - wrong = "acknowledgeing" and right = "acknowledging" - or - wrong = "ackward" and right = "awkward" - or - wrong = "ackward" and right = "backward" - or - wrong = "acommodate" and right = "accommodate" - or - wrong = "acomplish" and right = "accomplish" - or - wrong = "acomplished" and right = "accomplished" - or - wrong = "acomplishment" and right = "accomplishment" - or - wrong = "acomplishments" and right = "accomplishments" - or - wrong = "acording" and right = "according" - or - wrong = "acordingly" and right = "accordingly" - or - wrong = "acquaintence" and right = "acquaintance" - or - wrong = "acquaintences" and right = "acquaintances" - or - wrong = "acquiantence" and right = "acquaintance" - or - wrong = "acquiantences" and right = "acquaintances" - or - wrong = "acquited" and right = "acquitted" - or - wrong = "activites" and right = "activities" - or - wrong = "activly" and right = "actively" - or - wrong = "actualy" and right = "actually" - or - wrong = "acuracy" and right = "accuracy" - or - wrong = "acused" and right = "accused" - or - wrong = "acustom" and right = "accustom" - or - wrong = "acustommed" and right = "accustomed" - or - wrong = "adapater" and right = "adapter" - or - wrong = "adavanced" and right = "advanced" - or - wrong = "adbandon" and right = "abandon" - or - wrong = "addional" and right = "additional" - or - wrong = "addionally" and right = "additionally" - or - wrong = "additinally" and right = "additionally" - or - wrong = "additionaly" and right = "additionally" - or - wrong = "additonal" and right = "additional" - or - wrong = "additonally" and right = "additionally" - or - wrong = "addmission" and right = "admission" - or - wrong = "addopt" and right = "adopt" - or - wrong = "addopted" and right = "adopted" - or - wrong = "addoptive" and right = "adoptive" - or - wrong = "addres" and right = "adders" - or - wrong = "addres" and right = "address" - or - wrong = "addresable" and right = "addressable" - or - wrong = "addresed" and right = "addressed" - or - wrong = "addresing" and right = "addressing" - or - wrong = "addressess" and right = "addresses" - or - wrong = "addtion" and right = "addition" - or - wrong = "addtional" and right = "additional" - or - wrong = "adecuate" and right = "adequate" - or - wrong = "adequit" and right = "adequate" - or - wrong = "adhearing" and right = "adhering" - or - wrong = "adherance" and right = "adherence" - or - wrong = "admendment" and right = "amendment" - or - wrong = "admininistrative" and right = "administrative" - or - wrong = "adminstered" and right = "administered" - or - wrong = "adminstrate" and right = "administrate" - or - wrong = "adminstration" and right = "administration" - or - wrong = "adminstrative" and right = "administrative" - or - wrong = "adminstrator" and right = "administrator" - or - wrong = "admissability" and right = "admissibility" - or - wrong = "admissable" and right = "admissible" - or - wrong = "admited" and right = "admitted" - or - wrong = "admitedly" and right = "admittedly" - or - wrong = "adn" and right = "and" - or - wrong = "adolecent" and right = "adolescent" - or - wrong = "adquire" and right = "acquire" - or - wrong = "adquired" and right = "acquired" - or - wrong = "adquires" and right = "acquires" - or - wrong = "adquiring" and right = "acquiring" - or - wrong = "adres" and right = "address" - or - wrong = "adresable" and right = "addressable" - or - wrong = "adresing" and right = "addressing" - or - wrong = "adress" and right = "address" - or - wrong = "adressable" and right = "addressable" - or - wrong = "adressed" and right = "addressed" - or - wrong = "adressing" and right = "addressing" - or - wrong = "adressing" and right = "dressing" - or - wrong = "adventrous" and right = "adventurous" - or - wrong = "advertisment" and right = "advertisement" - or - wrong = "advertisments" and right = "advertisements" - or - wrong = "advesary" and right = "adversary" - or - wrong = "adviced" and right = "advised" - or - wrong = "aeriel" and right = "aerial" - or - wrong = "aeriels" and right = "aerials" - or - wrong = "afair" and right = "affair" - or - wrong = "afficianados" and right = "aficionados" - or - wrong = "afficionado" and right = "aficionado" - or - wrong = "afficionados" and right = "aficionados" - or - wrong = "affilate" and right = "affiliate" - or - wrong = "affilliate" and right = "affiliate" - or - wrong = "affort" and right = "afford" - or - wrong = "affort" and right = "effort" - or - wrong = "aforememtioned" and right = "aforementioned" - or - wrong = "againnst" and right = "against" - or - wrong = "agains" and right = "against" - or - wrong = "agaisnt" and right = "against" - or - wrong = "aganist" and right = "against" - or - wrong = "aggaravates" and right = "aggravates" - or - wrong = "aggreed" and right = "agreed" - or - wrong = "aggreement" and right = "agreement" - or - wrong = "aggregious" and right = "egregious" - or - wrong = "aggresive" and right = "aggressive" - or - wrong = "agian" and right = "again" - or - wrong = "agianst" and right = "against" - or - wrong = "agin" and right = "again" - or - wrong = "agina" and right = "again" - or - wrong = "agina" and right = "angina" - or - wrong = "aginst" and right = "against" - or - wrong = "agravate" and right = "aggravate" - or - wrong = "agre" and right = "agree" - or - wrong = "agred" and right = "agreed" - or - wrong = "agreeement" and right = "agreement" - or - wrong = "agreemnt" and right = "agreement" - or - wrong = "agregate" and right = "aggregate" - or - wrong = "agregates" and right = "aggregates" - or - wrong = "agreing" and right = "agreeing" - or - wrong = "agression" and right = "aggression" - or - wrong = "agressive" and right = "aggressive" - or - wrong = "agressively" and right = "aggressively" - or - wrong = "agressor" and right = "aggressor" - or - wrong = "agricultue" and right = "agriculture" - or - wrong = "agriculure" and right = "agriculture" - or - wrong = "agricuture" and right = "agriculture" - or - wrong = "agrieved" and right = "aggrieved" - or - wrong = "agrument" and right = "argument" - or - wrong = "agruments" and right = "arguments" - or - wrong = "ahev" and right = "have" - or - wrong = "ahppen" and right = "happen" - or - wrong = "ahve" and right = "have" - or - wrong = "aicraft" and right = "aircraft" - or - wrong = "aiport" and right = "airport" - or - wrong = "airbourne" and right = "airborne" - or - wrong = "aircaft" and right = "aircraft" - or - wrong = "aircrafts" and right = "aircraft" - or - wrong = "airporta" and right = "airports" - or - wrong = "airrcraft" and right = "aircraft" - or - wrong = "aisian" and right = "asian" - or - wrong = "albiet" and right = "albeit" - or - wrong = "alchohol" and right = "alcohol" - or - wrong = "alchoholic" and right = "alcoholic" - or - wrong = "alchol" and right = "alcohol" - or - wrong = "alcholic" and right = "alcoholic" - or - wrong = "alcohal" and right = "alcohol" - or - wrong = "alcoholical" and right = "alcoholic" - or - wrong = "aledge" and right = "allege" - or - wrong = "aledged" and right = "alleged" - or - wrong = "aledges" and right = "alleges" - or - wrong = "alege" and right = "allege" - or - wrong = "aleged" and right = "alleged" - or - wrong = "alegience" and right = "allegiance" - or - wrong = "algebraical" and right = "algebraic" - or - wrong = "algorhitms" and right = "algorithms" - or - wrong = "algoritm" and right = "algorithm" - or - wrong = "algoritms" and right = "algorithms" - or - wrong = "alientating" and right = "alienating" - or - wrong = "alledge" and right = "allege" - or - wrong = "alledged" and right = "alleged" - or - wrong = "alledgedly" and right = "allegedly" - or - wrong = "alledges" and right = "alleges" - or - wrong = "allegedely" and right = "allegedly" - or - wrong = "allegedy" and right = "allegedly" - or - wrong = "allegely" and right = "allegedly" - or - wrong = "allegence" and right = "allegiance" - or - wrong = "allegience" and right = "allegiance" - or - wrong = "allign" and right = "align" - or - wrong = "alligned" and right = "aligned" - or - wrong = "alliviate" and right = "alleviate" - or - wrong = "allopone" and right = "allophone" - or - wrong = "allopones" and right = "allophones" - or - wrong = "allready" and right = "already" - or - wrong = "allthough" and right = "although" - or - wrong = "alltogether" and right = "altogether" - or - wrong = "almsot" and right = "almost" - or - wrong = "alochol" and right = "alcohol" - or - wrong = "alomst" and right = "almost" - or - wrong = "alot" and right = "allot" - or - wrong = "alotted" and right = "allotted" - or - wrong = "alowed" and right = "allowed" - or - wrong = "alowing" and right = "allowing" - or - wrong = "alreayd" and right = "already" - or - wrong = "alse" and right = "else" - or - wrong = "alsot" and right = "also" - or - wrong = "alternitives" and right = "alternatives" - or - wrong = "altho" and right = "although" - or - wrong = "althought" and right = "although" - or - wrong = "altough" and right = "although" - or - wrong = "alusion" and right = "allusion" - or - wrong = "alusion" and right = "illusion" - or - wrong = "alwasy" and right = "always" - or - wrong = "alwyas" and right = "always" - or - wrong = "amalgomated" and right = "amalgamated" - or - wrong = "amatuer" and right = "amateur" - or - wrong = "amature" and right = "amateur" - or - wrong = "amature" and right = "armature" - or - wrong = "amendmant" and right = "amendment" - or - wrong = "amercia" and right = "america" - or - wrong = "amerliorate" and right = "ameliorate" - or - wrong = "amke" and right = "make" - or - wrong = "amking" and right = "making" - or - wrong = "ammend" and right = "amend" - or - wrong = "ammended" and right = "amended" - or - wrong = "ammendment" and right = "amendment" - or - wrong = "ammendments" and right = "amendments" - or - wrong = "ammount" and right = "amount" - or - wrong = "ammused" and right = "amused" - or - wrong = "amoung" and right = "among" - or - wrong = "amoungst" and right = "amongst" - or - wrong = "amung" and right = "among" - or - wrong = "amunition" and right = "ammunition" - or - wrong = "analagous" and right = "analogous" - or - wrong = "analitic" and right = "analytic" - or - wrong = "analogeous" and right = "analogous" - or - wrong = "anarchim" and right = "anarchism" - or - wrong = "anarchistm" and right = "anarchism" - or - wrong = "anbd" and right = "and" - or - wrong = "ancestory" and right = "ancestry" - or - wrong = "ancilliary" and right = "ancillary" - or - wrong = "andd" and right = "and" - or - wrong = "androgenous" and right = "androgynous" - or - wrong = "androgeny" and right = "androgyny" - or - wrong = "anihilation" and right = "annihilation" - or - wrong = "aniversary" and right = "anniversary" - or - wrong = "annoint" and right = "anoint" - or - wrong = "annointed" and right = "anointed" - or - wrong = "annointing" and right = "anointing" - or - wrong = "annoints" and right = "anoints" - or - wrong = "annouced" and right = "announced" - or - wrong = "annualy" and right = "annually" - or - wrong = "annuled" and right = "annulled" - or - wrong = "anohter" and right = "another" - or - wrong = "anomolies" and right = "anomalies" - or - wrong = "anomolous" and right = "anomalous" - or - wrong = "anomoly" and right = "anomaly" - or - wrong = "anonimity" and right = "anonymity" - or - wrong = "anounced" and right = "announced" - or - wrong = "anouncement" and right = "announcement" - or - wrong = "ansalisation" and right = "nasalisation" - or - wrong = "ansalization" and right = "nasalization" - or - wrong = "ansestors" and right = "ancestors" - or - wrong = "antartic" and right = "antarctic" - or - wrong = "anthromorphization" and right = "anthropomorphization" - or - wrong = "anthropolgist" and right = "anthropologist" - or - wrong = "anthropolgy" and right = "anthropology" - or - wrong = "anual" and right = "annual" - or - wrong = "anulled" and right = "annulled" - or - wrong = "anwsered" and right = "answered" - or - wrong = "anyhwere" and right = "anywhere" - or - wrong = "anytying" and right = "anything" - or - wrong = "aparent" and right = "apparent" - or - wrong = "aparment" and right = "apartment" - or - wrong = "apenines" and right = "apennines" - or - wrong = "aplication" and right = "application" - or - wrong = "aplied" and right = "applied" - or - wrong = "apolegetics" and right = "apologetics" - or - wrong = "apon" and right = "apron" - or - wrong = "apon" and right = "upon" - or - wrong = "apparant" and right = "apparent" - or - wrong = "apparantly" and right = "apparently" - or - wrong = "appart" and right = "apart" - or - wrong = "appartment" and right = "apartment" - or - wrong = "appartments" and right = "apartments" - or - wrong = "appealling" and right = "appalling" - or - wrong = "appealling" and right = "appealing" - or - wrong = "appeareance" and right = "appearance" - or - wrong = "appearence" and right = "appearance" - or - wrong = "appearences" and right = "appearances" - or - wrong = "appenines" and right = "apennines" - or - wrong = "apperance" and right = "appearance" - or - wrong = "apperances" and right = "appearances" - or - wrong = "appereance" and right = "appearance" - or - wrong = "appereances" and right = "appearances" - or - wrong = "applicaiton" and right = "application" - or - wrong = "applicaitons" and right = "applications" - or - wrong = "appologies" and right = "apologies" - or - wrong = "appology" and right = "apology" - or - wrong = "apprearance" and right = "appearance" - or - wrong = "apprieciate" and right = "appreciate" - or - wrong = "approachs" and right = "approaches" - or - wrong = "appropiate" and right = "appropriate" - or - wrong = "appropraite" and right = "appropriate" - or - wrong = "appropropiate" and right = "appropriate" - or - wrong = "approproximate" and right = "approximate" - or - wrong = "approxamately" and right = "approximately" - or - wrong = "approxiately" and right = "approximately" - or - wrong = "approximitely" and right = "approximately" - or - wrong = "aprehensive" and right = "apprehensive" - or - wrong = "apropriate" and right = "appropriate" - or - wrong = "aproval" and right = "approval" - or - wrong = "aproximate" and right = "approximate" - or - wrong = "aproximately" and right = "approximately" - or - wrong = "aquaduct" and right = "aqueduct" - or - wrong = "aquaintance" and right = "acquaintance" - or - wrong = "aquainted" and right = "acquainted" - or - wrong = "aquiantance" and right = "acquaintance" - or - wrong = "aquire" and right = "acquire" - or - wrong = "aquired" and right = "acquired" - or - wrong = "aquiring" and right = "acquiring" - or - wrong = "aquisition" and right = "acquisition" - or - wrong = "aquitted" and right = "acquitted" - or - wrong = "aranged" and right = "arranged" - or - wrong = "arangement" and right = "arrangement" - or - wrong = "arbitarily" and right = "arbitrarily" - or - wrong = "arbitary" and right = "arbitrary" - or - wrong = "archaelogical" and right = "archaeological" - or - wrong = "archaelogists" and right = "archaeologists" - or - wrong = "archaelogy" and right = "archaeology" - or - wrong = "archaoelogy" and right = "archaeology" - or - wrong = "archaoelogy" and right = "archeology" - or - wrong = "archaology" and right = "archaeology" - or - wrong = "archaology" and right = "archeology" - or - wrong = "archeaologist" and right = "archaeologist" - or - wrong = "archeaologist" and right = "archeologist" - or - wrong = "archeaologists" and right = "archaeologists" - or - wrong = "archeaologists" and right = "archeologists" - or - wrong = "archetect" and right = "architect" - or - wrong = "archetects" and right = "architects" - or - wrong = "archetectural" and right = "architectural" - or - wrong = "archetecturally" and right = "architecturally" - or - wrong = "archetecture" and right = "architecture" - or - wrong = "archiac" and right = "archaic" - or - wrong = "archictect" and right = "architect" - or - wrong = "archimedian" and right = "archimedean" - or - wrong = "architecht" and right = "architect" - or - wrong = "architechturally" and right = "architecturally" - or - wrong = "architechture" and right = "architecture" - or - wrong = "architechtures" and right = "architectures" - or - wrong = "architectual" and right = "architectural" - or - wrong = "archtype" and right = "archetype" - or - wrong = "archtypes" and right = "archetypes" - or - wrong = "aready" and right = "already" - or - wrong = "areodynamics" and right = "aerodynamics" - or - wrong = "argubly" and right = "arguably" - or - wrong = "arguement" and right = "argument" - or - wrong = "arguements" and right = "arguments" - or - wrong = "arised" and right = "arose" - or - wrong = "arival" and right = "arrival" - or - wrong = "armamant" and right = "armament" - or - wrong = "armistace" and right = "armistice" - or - wrong = "arogant" and right = "arrogant" - or - wrong = "arogent" and right = "arrogant" - or - wrong = "aroud" and right = "around" - or - wrong = "arrangment" and right = "arrangement" - or - wrong = "arrangments" and right = "arrangements" - or - wrong = "arrengement" and right = "arrangement" - or - wrong = "arrengements" and right = "arrangements" - or - wrong = "arround" and right = "around" - or - wrong = "artcile" and right = "article" - or - wrong = "artical" and right = "article" - or - wrong = "artice" and right = "article" - or - wrong = "articel" and right = "article" - or - wrong = "artifical" and right = "artificial" - or - wrong = "artifically" and right = "artificially" - or - wrong = "artillary" and right = "artillery" - or - wrong = "arund" and right = "around" - or - wrong = "asetic" and right = "ascetic" - or - wrong = "asign" and right = "assign" - or - wrong = "aslo" and right = "also" - or - wrong = "asnyc" and right = "async" - or - wrong = "asociated" and right = "associated" - or - wrong = "asorbed" and right = "absorbed" - or - wrong = "asphyxation" and right = "asphyxiation" - or - wrong = "assasin" and right = "assassin" - or - wrong = "assasinate" and right = "assassinate" - or - wrong = "assasinated" and right = "assassinated" - or - wrong = "assasinates" and right = "assassinates" - or - wrong = "assasination" and right = "assassination" - or - wrong = "assasinations" and right = "assassinations" - or - wrong = "assasined" and right = "assassinated" - or - wrong = "assasins" and right = "assassins" - or - wrong = "assassintation" and right = "assassination" - or - wrong = "assemple" and right = "assemble" - or - wrong = "assertation" and right = "assertion" - or - wrong = "asside" and right = "aside" - or - wrong = "assisnate" and right = "assassinate" - or - wrong = "assit" and right = "assist" - or - wrong = "assitant" and right = "assistant" - or - wrong = "assocation" and right = "association" - or - wrong = "assoicate" and right = "associate" - or - wrong = "assoicated" and right = "associated" - or - wrong = "assoicates" and right = "associates" - or - wrong = "assosication" and right = "assassination" - or - wrong = "asssassans" and right = "assassins" - or - wrong = "assualt" and right = "assault" - or - wrong = "assualted" and right = "assaulted" - or - wrong = "assymetric" and right = "asymmetric" - or - wrong = "assymetrical" and right = "asymmetrical" - or - wrong = "asteriod" and right = "asteroid" - or - wrong = "asthetic" and right = "aesthetic" - or - wrong = "asthetical" and right = "aesthetical" - or - wrong = "asthetically" and right = "aesthetically" - or - wrong = "asume" and right = "assume" - or - wrong = "atain" and right = "attain" - or - wrong = "atempting" and right = "attempting" - or - wrong = "atheistical" and right = "atheistic" - or - wrong = "athenean" and right = "athenian" - or - wrong = "atheneans" and right = "athenians" - or - wrong = "athiesm" and right = "atheism" - or - wrong = "athiest" and right = "atheist" - or - wrong = "atorney" and right = "attorney" - or - wrong = "atribute" and right = "attribute" - or - wrong = "atributed" and right = "attributed" - or - wrong = "atributes" and right = "attributes" - or - wrong = "attaindre" and right = "attainder" - or - wrong = "attaindre" and right = "attained" - or - wrong = "attemp" and right = "attempt" - or - wrong = "attemped" and right = "attempted" - or - wrong = "attemt" and right = "attempt" - or - wrong = "attemted" and right = "attempted" - or - wrong = "attemting" and right = "attempting" - or - wrong = "attemts" and right = "attempts" - or - wrong = "attendence" and right = "attendance" - or - wrong = "attendent" and right = "attendant" - or - wrong = "attendents" and right = "attendants" - or - wrong = "attened" and right = "attended" - or - wrong = "attension" and right = "attention" - or - wrong = "attitide" and right = "attitude" - or - wrong = "attributred" and right = "attributed" - or - wrong = "attrocities" and right = "atrocities" - or - wrong = "audeince" and right = "audience" - or - wrong = "auromated" and right = "automated" - or - wrong = "austrailia" and right = "australia" - or - wrong = "austrailian" and right = "australian" - or - wrong = "auther" and right = "author" - or - wrong = "authobiographic" and right = "autobiographic" - or - wrong = "authobiography" and right = "autobiography" - or - wrong = "authorative" and right = "authoritative" - or - wrong = "authorites" and right = "authorities" - or - wrong = "authorithy" and right = "authority" - or - wrong = "authoritiers" and right = "authorities" - or - wrong = "authoritive" and right = "authoritative" - or - wrong = "authrorities" and right = "authorities" - or - wrong = "autochtonous" and right = "autochthonous" - or - wrong = "autoctonous" and right = "autochthonous" - or - wrong = "automaticly" and right = "automatically" - or - wrong = "automibile" and right = "automobile" - or - wrong = "automonomous" and right = "autonomous" - or - wrong = "autor" and right = "author" - or - wrong = "autority" and right = "authority" - or - wrong = "auxilary" and right = "auxiliary" - or - wrong = "auxillaries" and right = "auxiliaries" - or - wrong = "auxillary" and right = "auxiliary" - or - wrong = "auxilliaries" and right = "auxiliaries" - or - wrong = "auxilliary" and right = "auxiliary" - or - wrong = "availabe" and right = "available" - or - wrong = "availablity" and right = "availability" - or - wrong = "availaible" and right = "available" - or - wrong = "availble" and right = "available" - or - wrong = "availiable" and right = "available" - or - wrong = "availible" and right = "available" - or - wrong = "avalable" and right = "available" - or - wrong = "avalance" and right = "avalanche" - or - wrong = "avaliable" and right = "available" - or - wrong = "avation" and right = "aviation" - or - wrong = "averageed" and right = "averaged" - or - wrong = "avilable" and right = "available" - or - wrong = "awared" and right = "awarded" - or - wrong = "awya" and right = "away" - or - wrong = "baceause" and right = "because" - or - wrong = "backgorund" and right = "background" - or - wrong = "backrounds" and right = "backgrounds" - or - wrong = "bakc" and right = "back" - or - wrong = "banannas" and right = "bananas" - or - wrong = "bandwith" and right = "bandwidth" - or - wrong = "bankrupcy" and right = "bankruptcy" - or - wrong = "banruptcy" and right = "bankruptcy" - or - wrong = "baout" and right = "about" - or - wrong = "baout" and right = "bout" - or - wrong = "basicaly" and right = "basically" - or - wrong = "basicly" and right = "basically" - or - wrong = "bcak" and right = "back" - or - wrong = "beachead" and right = "beachhead" - or - wrong = "beacuse" and right = "because" - or - wrong = "beastiality" and right = "bestiality" - or - wrong = "beatiful" and right = "beautiful" - or - wrong = "beaurocracy" and right = "bureaucracy" - or - wrong = "beaurocratic" and right = "bureaucratic" - or - wrong = "beautyfull" and right = "beautiful" - or - wrong = "becamae" and right = "became" - or - wrong = "becames" and right = "became" - or - wrong = "becames" and right = "becomes" - or - wrong = "becasue" and right = "because" - or - wrong = "beccause" and right = "because" - or - wrong = "becomeing" and right = "becoming" - or - wrong = "becomming" and right = "becoming" - or - wrong = "becouse" and right = "because" - or - wrong = "becuase" and right = "because" - or - wrong = "bedore" and right = "before" - or - wrong = "beeing" and right = "being" - or - wrong = "befoer" and right = "before" - or - wrong = "beggin" and right = "begging" - or - wrong = "beggin" and right = "begin" - or - wrong = "begginer" and right = "beginner" - or - wrong = "begginers" and right = "beginners" - or - wrong = "beggining" and right = "beginning" - or - wrong = "begginings" and right = "beginnings" - or - wrong = "beggins" and right = "begins" - or - wrong = "begining" and right = "beginning" - or - wrong = "beginnig" and right = "beginning" - or - wrong = "behavour" and right = "behavior" - or - wrong = "behavour" and right = "behaviour" - or - wrong = "beleagured" and right = "beleaguered" - or - wrong = "beleif" and right = "belief" - or - wrong = "beleive" and right = "believe" - or - wrong = "beleived" and right = "believed" - or - wrong = "beleives" and right = "believes" - or - wrong = "beleiving" and right = "believing" - or - wrong = "beligum" and right = "belgium" - or - wrong = "belive" and right = "believe" - or - wrong = "belived" and right = "believed" - or - wrong = "belived" and right = "beloved" - or - wrong = "belives" and right = "beliefs" - or - wrong = "belives" and right = "believes" - or - wrong = "belligerant" and right = "belligerent" - or - wrong = "bellweather" and right = "bellwether" - or - wrong = "bemusemnt" and right = "bemusement" - or - wrong = "beneficary" and right = "beneficiary" - or - wrong = "beng" and right = "being" - or - wrong = "benificial" and right = "beneficial" - or - wrong = "benifit" and right = "benefit" - or - wrong = "benifits" and right = "benefits" - or - wrong = "bergamont" and right = "bergamot" - or - wrong = "bernouilli" and right = "bernoulli" - or - wrong = "beseige" and right = "besiege" - or - wrong = "beseiged" and right = "besieged" - or - wrong = "beseiging" and right = "besieging" - or - wrong = "beteen" and right = "between" - or - wrong = "betwen" and right = "between" - or - wrong = "beween" and right = "between" - or - wrong = "bewteen" and right = "between" - or - wrong = "bigining" and right = "beginning" - or - wrong = "biginning" and right = "beginning" - or - wrong = "bilateraly" and right = "bilaterally" - or - wrong = "billingualism" and right = "bilingualism" - or - wrong = "binominal" and right = "binomial" - or - wrong = "bizzare" and right = "bizarre" - or - wrong = "blaim" and right = "blame" - or - wrong = "blaimed" and right = "blamed" - or - wrong = "blessure" and right = "blessing" - or - wrong = "blitzkreig" and right = "blitzkrieg" - or - wrong = "boaut" and right = "about" - or - wrong = "boaut" and right = "boat" - or - wrong = "boaut" and right = "bout" - or - wrong = "bodydbuilder" and right = "bodybuilder" - or - wrong = "bolean" and right = "boolean" - or - wrong = "bombardement" and right = "bombardment" - or - wrong = "bombarment" and right = "bombardment" - or - wrong = "bondary" and right = "boundary" - or - wrong = "bonnano" and right = "bonanno" - or - wrong = "boook" and right = "book" - or - wrong = "borke" and right = "broke" - or - wrong = "boundry" and right = "boundary" - or - wrong = "bouyancy" and right = "buoyancy" - or - wrong = "bouyant" and right = "buoyant" - or - wrong = "boyant" and right = "buoyant" - or - wrong = "bradcast" and right = "broadcast" - or - wrong = "brasillian" and right = "brazilian" - or - wrong = "breakthough" and right = "breakthrough" - or - wrong = "breakthroughts" and right = "breakthroughs" - or - wrong = "breif" and right = "brief" - or - wrong = "breifly" and right = "briefly" - or - wrong = "brethen" and right = "brethren" - or - wrong = "bretheren" and right = "brethren" - or - wrong = "briliant" and right = "brilliant" - or - wrong = "brillant" and right = "brilliant" - or - wrong = "brimestone" and right = "brimstone" - or - wrong = "britian" and right = "britain" - or - wrong = "brittish" and right = "british" - or - wrong = "broacasted" and right = "broadcast" - or - wrong = "broadacasting" and right = "broadcasting" - or - wrong = "broady" and right = "broadly" - or - wrong = "buddah" and right = "buddha" - or - wrong = "buddist" and right = "buddhist" - or - wrong = "buisness" and right = "business" - or - wrong = "buisnessman" and right = "businessman" - or - wrong = "buoancy" and right = "buoyancy" - or - wrong = "buring" and right = "burin" - or - wrong = "buring" and right = "burning" - or - wrong = "buring" and right = "burying" - or - wrong = "buring" and right = "during" - or - wrong = "burried" and right = "buried" - or - wrong = "busines" and right = "business" - or - wrong = "busineses" and right = "business" - or - wrong = "busineses" and right = "businesses" - or - wrong = "busness" and right = "business" - or - wrong = "bussiness" and right = "business" - or - wrong = "caculater" and right = "calculator" - or - wrong = "cacuses" and right = "caucuses" - or - wrong = "cahracters" and right = "characters" - or - wrong = "calaber" and right = "caliber" - or - wrong = "calander" and right = "calendar" - or - wrong = "calander" and right = "calender" - or - wrong = "calander" and right = "colander" - or - wrong = "calculater" and right = "calculator" - or - wrong = "calculs" and right = "calculus" - or - wrong = "calender" and right = "calendar" - or - wrong = "calenders" and right = "calendars" - or - wrong = "caligraphy" and right = "calligraphy" - or - wrong = "caluclate" and right = "calculate" - or - wrong = "caluclated" and right = "calculated" - or - wrong = "caluculate" and right = "calculate" - or - wrong = "caluculated" and right = "calculated" - or - wrong = "calulate" and right = "calculate" - or - wrong = "calulated" and right = "calculated" - or - wrong = "calulater" and right = "calculator" - or - wrong = "cambrige" and right = "cambridge" - or - wrong = "camoflage" and right = "camouflage" - or - wrong = "campagin" and right = "campaign" - or - wrong = "campain" and right = "campaign" - or - wrong = "campains" and right = "campaigns" - or - wrong = "candadate" and right = "candidate" - or - wrong = "candiate" and right = "candidate" - or - wrong = "candidiate" and right = "candidate" - or - wrong = "cannister" and right = "canister" - or - wrong = "cannisters" and right = "canisters" - or - wrong = "cannnot" and right = "cannot" - or - wrong = "cannonical" and right = "canonical" - or - wrong = "cannotation" and right = "connotation" - or - wrong = "cannotations" and right = "connotations" - or - wrong = "caost" and right = "coast" - or - wrong = "caperbility" and right = "capability" - or - wrong = "capible" and right = "capable" - or - wrong = "captial" and right = "capital" - or - wrong = "captued" and right = "captured" - or - wrong = "capturd" and right = "captured" - or - wrong = "carachter" and right = "character" - or - wrong = "caracterized" and right = "characterized" - or - wrong = "carcas" and right = "caracas" - or - wrong = "carcas" and right = "carcass" - or - wrong = "carefull" and right = "careful" - or - wrong = "careing" and right = "caring" - or - wrong = "carismatic" and right = "charismatic" - or - wrong = "carmalite" and right = "carmelite" - or - wrong = "carnagie" and right = "carnegie" - or - wrong = "carnege" and right = "carnage" - or - wrong = "carnege" and right = "carnegie" - or - wrong = "carnige" and right = "carnage" - or - wrong = "carnige" and right = "carnegie" - or - wrong = "carnigie" and right = "carnegie" - or - wrong = "carreer" and right = "career" - or - wrong = "carrers" and right = "careers" - or - wrong = "carribbean" and right = "caribbean" - or - wrong = "carribean" and right = "caribbean" - or - wrong = "carryng" and right = "carrying" - or - wrong = "cartdridge" and right = "cartridge" - or - wrong = "carthagian" and right = "carthaginian" - or - wrong = "carthographer" and right = "cartographer" - or - wrong = "cartilege" and right = "cartilage" - or - wrong = "cartilidge" and right = "cartilage" - or - wrong = "cartrige" and right = "cartridge" - or - wrong = "casette" and right = "cassette" - or - wrong = "casion" and right = "caisson" - or - wrong = "cassawory" and right = "cassowary" - or - wrong = "cassowarry" and right = "cassowary" - or - wrong = "casue" and right = "cause" - or - wrong = "casued" and right = "caused" - or - wrong = "casues" and right = "causes" - or - wrong = "casuing" and right = "causing" - or - wrong = "casulaties" and right = "casualties" - or - wrong = "casulaty" and right = "casualty" - or - wrong = "catagories" and right = "categories" - or - wrong = "catagorized" and right = "categorized" - or - wrong = "catagory" and right = "category" - or - wrong = "cataline" and right = "catalina" - or - wrong = "cataline" and right = "catiline" - or - wrong = "catapillar" and right = "caterpillar" - or - wrong = "catapillars" and right = "caterpillars" - or - wrong = "catapiller" and right = "caterpillar" - or - wrong = "catapillers" and right = "caterpillars" - or - wrong = "catepillar" and right = "caterpillar" - or - wrong = "catepillars" and right = "caterpillars" - or - wrong = "catergorize" and right = "categorize" - or - wrong = "catergorized" and right = "categorized" - or - wrong = "caterpilar" and right = "caterpillar" - or - wrong = "caterpilars" and right = "caterpillars" - or - wrong = "caterpiller" and right = "caterpillar" - or - wrong = "caterpillers" and right = "caterpillars" - or - wrong = "cathlic" and right = "catholic" - or - wrong = "catholocism" and right = "catholicism" - or - wrong = "catterpilar" and right = "caterpillar" - or - wrong = "catterpilars" and right = "caterpillars" - or - wrong = "catterpillar" and right = "caterpillar" - or - wrong = "catterpillars" and right = "caterpillars" - or - wrong = "cattleship" and right = "battleship" - or - wrong = "causalities" and right = "casualties" - or - wrong = "ceasar" and right = "caesar" - or - wrong = "celcius" and right = "celsius" - or - wrong = "cellpading" and right = "cellpadding" - or - wrong = "cementary" and right = "cemetery" - or - wrong = "cemetarey" and right = "cemetery" - or - wrong = "cemetaries" and right = "cemeteries" - or - wrong = "cemetary" and right = "cemetery" - or - wrong = "cencus" and right = "census" - or - wrong = "censur" and right = "censor" - or - wrong = "censur" and right = "censure" - or - wrong = "cententenial" and right = "centennial" - or - wrong = "centruies" and right = "centuries" - or - wrong = "centruy" and right = "century" - or - wrong = "centuties" and right = "centuries" - or - wrong = "centuty" and right = "century" - or - wrong = "ceratin" and right = "certain" - or - wrong = "ceratin" and right = "keratin" - or - wrong = "cerimonial" and right = "ceremonial" - or - wrong = "cerimonies" and right = "ceremonies" - or - wrong = "cerimonious" and right = "ceremonious" - or - wrong = "cerimony" and right = "ceremony" - or - wrong = "ceromony" and right = "ceremony" - or - wrong = "certainity" and right = "certainty" - or - wrong = "certian" and right = "certain" - or - wrong = "cervial" and right = "cervical" - or - wrong = "cervial" and right = "serval" - or - wrong = "cervial" and right = "servile" - or - wrong = "chalenging" and right = "challenging" - or - wrong = "challange" and right = "challenge" - or - wrong = "challanged" and right = "challenged" - or - wrong = "challege" and right = "challenge" - or - wrong = "champange" and right = "champagne" - or - wrong = "changable" and right = "changeable" - or - wrong = "charachter" and right = "character" - or - wrong = "charachters" and right = "characters" - or - wrong = "charactersistic" and right = "characteristic" - or - wrong = "charactor" and right = "character" - or - wrong = "charactors" and right = "characters" - or - wrong = "charasmatic" and right = "charismatic" - or - wrong = "charaterized" and right = "characterized" - or - wrong = "chariman" and right = "chairman" - or - wrong = "charistics" and right = "characteristics" - or - wrong = "chasr" and right = "chase" - or - wrong = "chasr" and right = "chaser" - or - wrong = "cheif" and right = "chief" - or - wrong = "cheifs" and right = "chiefs" - or - wrong = "chemcial" and right = "chemical" - or - wrong = "chemcially" and right = "chemically" - or - wrong = "chemestry" and right = "chemistry" - or - wrong = "chemicaly" and right = "chemically" - or - wrong = "childbird" and right = "childbirth" - or - wrong = "childen" and right = "children" - or - wrong = "choclate" and right = "chocolate" - or - wrong = "choosen" and right = "chosen" - or - wrong = "chracter" and right = "character" - or - wrong = "chuch" and right = "church" - or - wrong = "churchs" and right = "churches" - or - wrong = "cincinatti" and right = "cincinnati" - or - wrong = "cincinnatti" and right = "cincinnati" - or - wrong = "circulaton" and right = "circulation" - or - wrong = "circumsicion" and right = "circumcision" - or - wrong = "circut" and right = "circuit" - or - wrong = "ciricuit" and right = "circuit" - or - wrong = "ciriculum" and right = "curriculum" - or - wrong = "civillian" and right = "civilian" - or - wrong = "claer" and right = "clear" - or - wrong = "claerer" and right = "clearer" - or - wrong = "claerly" and right = "clearly" - or - wrong = "claimes" and right = "claims" - or - wrong = "clas" and right = "class" - or - wrong = "clasic" and right = "classic" - or - wrong = "clasical" and right = "classical" - or - wrong = "clasically" and right = "classically" - or - wrong = "cleareance" and right = "clearance" - or - wrong = "clera" and right = "clear" - or - wrong = "clera" and right = "sclera" - or - wrong = "clincial" and right = "clinical" - or - wrong = "clinicaly" and right = "clinically" - or - wrong = "cmo" and right = "com" - or - wrong = "cmoputer" and right = "computer" - or - wrong = "coctail" and right = "cocktail" - or - wrong = "coform" and right = "conform" - or - wrong = "cognizent" and right = "cognizant" - or - wrong = "coincedentally" and right = "coincidentally" - or - wrong = "colaborations" and right = "collaborations" - or - wrong = "colateral" and right = "collateral" - or - wrong = "colelctive" and right = "collective" - or - wrong = "collaberative" and right = "collaborative" - or - wrong = "collecton" and right = "collection" - or - wrong = "collegue" and right = "colleague" - or - wrong = "collegues" and right = "colleagues" - or - wrong = "collonade" and right = "colonnade" - or - wrong = "collonies" and right = "colonies" - or - wrong = "collony" and right = "colony" - or - wrong = "collosal" and right = "colossal" - or - wrong = "colonizators" and right = "colonizers" - or - wrong = "comander" and right = "commandeer" - or - wrong = "comander" and right = "commander" - or - wrong = "comando" and right = "commando" - or - wrong = "comandos" and right = "commandos" - or - wrong = "comany" and right = "company" - or - wrong = "comapany" and right = "company" - or - wrong = "comback" and right = "comeback" - or - wrong = "combanations" and right = "combinations" - or - wrong = "combinatins" and right = "combinations" - or - wrong = "combusion" and right = "combustion" - or - wrong = "comdemnation" and right = "condemnation" - or - wrong = "comemmorates" and right = "commemorates" - or - wrong = "comemoretion" and right = "commemoration" - or - wrong = "comision" and right = "commission" - or - wrong = "comisioned" and right = "commissioned" - or - wrong = "comisioner" and right = "commissioner" - or - wrong = "comisioning" and right = "commissioning" - or - wrong = "comisions" and right = "commissions" - or - wrong = "comission" and right = "commission" - or - wrong = "comissioned" and right = "commissioned" - or - wrong = "comissioner" and right = "commissioner" - or - wrong = "comissioning" and right = "commissioning" - or - wrong = "comissions" and right = "commissions" - or - wrong = "comited" and right = "committed" - or - wrong = "comiting" and right = "committing" - or - wrong = "comitted" and right = "committed" - or - wrong = "comittee" and right = "committee" - or - wrong = "comitting" and right = "committing" - or - wrong = "commandoes" and right = "commandos" - or - wrong = "commedic" and right = "comedic" - or - wrong = "commemerative" and right = "commemorative" - or - wrong = "commemmorate" and right = "commemorate" - or - wrong = "commemmorating" and right = "commemorating" - or - wrong = "commerical" and right = "commercial" - or - wrong = "commerically" and right = "commercially" - or - wrong = "commericial" and right = "commercial" - or - wrong = "commericially" and right = "commercially" - or - wrong = "commerorative" and right = "commemorative" - or - wrong = "comming" and right = "coming" - or - wrong = "comminication" and right = "communication" - or - wrong = "commision" and right = "commission" - or - wrong = "commisioned" and right = "commissioned" - or - wrong = "commisioner" and right = "commissioner" - or - wrong = "commisioning" and right = "commissioning" - or - wrong = "commisions" and right = "commissions" - or - wrong = "commited" and right = "committed" - or - wrong = "commitee" and right = "committee" - or - wrong = "commiting" and right = "committing" - or - wrong = "committe" and right = "committee" - or - wrong = "committment" and right = "commitment" - or - wrong = "committments" and right = "commitments" - or - wrong = "commmemorated" and right = "commemorated" - or - wrong = "commongly" and right = "commonly" - or - wrong = "commonweath" and right = "commonwealth" - or - wrong = "commuications" and right = "communications" - or - wrong = "commuinications" and right = "communications" - or - wrong = "communciation" and right = "communication" - or - wrong = "communiation" and right = "communication" - or - wrong = "communites" and right = "communities" - or - wrong = "compability" and right = "compatibility" - or - wrong = "comparision" and right = "comparison" - or - wrong = "comparisions" and right = "comparisons" - or - wrong = "comparitive" and right = "comparative" - or - wrong = "comparitively" and right = "comparatively" - or - wrong = "compatabilities" and right = "compatibilities" - or - wrong = "compatability" and right = "compatibility" - or - wrong = "compatable" and right = "compatible" - or - wrong = "compatablities" and right = "compatibilities" - or - wrong = "compatablity" and right = "compatibility" - or - wrong = "compatiable" and right = "compatible" - or - wrong = "compatiblities" and right = "compatibilities" - or - wrong = "compatiblity" and right = "compatibility" - or - wrong = "compeitions" and right = "competitions" - or - wrong = "compensantion" and right = "compensation" - or - wrong = "competance" and right = "competence" - or - wrong = "competant" and right = "competent" - or - wrong = "competative" and right = "competitive" - or - wrong = "competion" and right = "competition" - or - wrong = "competion" and right = "completion" - or - wrong = "competitiion" and right = "competition" - or - wrong = "competive" and right = "competitive" - or - wrong = "competiveness" and right = "competitiveness" - or - wrong = "comphrehensive" and right = "comprehensive" - or - wrong = "compitent" and right = "competent" - or - wrong = "completelyl" and right = "completely" - or - wrong = "completetion" and right = "completion" - or - wrong = "complier" and right = "compiler" - or - wrong = "componant" and right = "component" - or - wrong = "comprable" and right = "comparable" - or - wrong = "comprimise" and right = "compromise" - or - wrong = "compulsary" and right = "compulsory" - or - wrong = "compulsery" and right = "compulsory" - or - wrong = "computarized" and right = "computerized" - or - wrong = "concensus" and right = "consensus" - or - wrong = "concider" and right = "consider" - or - wrong = "concidered" and right = "considered" - or - wrong = "concidering" and right = "considering" - or - wrong = "conciders" and right = "considers" - or - wrong = "concieted" and right = "conceited" - or - wrong = "concieved" and right = "conceived" - or - wrong = "concious" and right = "conscious" - or - wrong = "conciously" and right = "consciously" - or - wrong = "conciousness" and right = "consciousness" - or - wrong = "condamned" and right = "condemned" - or - wrong = "condemmed" and right = "condemned" - or - wrong = "condidtion" and right = "condition" - or - wrong = "condidtions" and right = "conditions" - or - wrong = "conected" and right = "connected" - or - wrong = "conection" and right = "connection" - or - wrong = "conesencus" and right = "consensus" - or - wrong = "confidental" and right = "confidential" - or - wrong = "confidentally" and right = "confidentially" - or - wrong = "confids" and right = "confides" - or - wrong = "configureable" and right = "configurable" - or - wrong = "confortable" and right = "comfortable" - or - wrong = "congradulations" and right = "congratulations" - or - wrong = "congresional" and right = "congressional" - or - wrong = "conived" and right = "connived" - or - wrong = "conjecutre" and right = "conjecture" - or - wrong = "conjuction" and right = "conjunction" - or - wrong = "connectinos" and right = "connections" - or - wrong = "conneticut" and right = "connecticut" - or - wrong = "conotations" and right = "connotations" - or - wrong = "conquerd" and right = "conquered" - or - wrong = "conquerer" and right = "conqueror" - or - wrong = "conquerers" and right = "conquerors" - or - wrong = "conqured" and right = "conquered" - or - wrong = "conscent" and right = "consent" - or - wrong = "consciouness" and right = "consciousness" - or - wrong = "consdider" and right = "consider" - or - wrong = "consdidered" and right = "considered" - or - wrong = "consdiered" and right = "considered" - or - wrong = "consectutive" and right = "consecutive" - or - wrong = "consenquently" and right = "consequently" - or - wrong = "consentrate" and right = "concentrate" - or - wrong = "consentrated" and right = "concentrated" - or - wrong = "consentrates" and right = "concentrates" - or - wrong = "consept" and right = "concept" - or - wrong = "consequentually" and right = "consequently" - or - wrong = "consequeseces" and right = "consequences" - or - wrong = "consern" and right = "concern" - or - wrong = "conserned" and right = "concerned" - or - wrong = "conserning" and right = "concerning" - or - wrong = "conservitive" and right = "conservative" - or - wrong = "consiciousness" and right = "consciousness" - or - wrong = "consicousness" and right = "consciousness" - or - wrong = "considerd" and right = "considered" - or - wrong = "consideres" and right = "considered" - or - wrong = "consious" and right = "conscious" - or - wrong = "consistant" and right = "consistent" - or - wrong = "consistantly" and right = "consistently" - or - wrong = "consituencies" and right = "constituencies" - or - wrong = "consituency" and right = "constituency" - or - wrong = "consituted" and right = "constituted" - or - wrong = "consitution" and right = "constitution" - or - wrong = "consitutional" and right = "constitutional" - or - wrong = "consolodate" and right = "consolidate" - or - wrong = "consolodated" and right = "consolidated" - or - wrong = "consonent" and right = "consonant" - or - wrong = "consonents" and right = "consonants" - or - wrong = "consorcium" and right = "consortium" - or - wrong = "conspiracys" and right = "conspiracies" - or - wrong = "conspiriator" and right = "conspirator" - or - wrong = "consructor" and right = "constructor" - or - wrong = "constaints" and right = "constraints" - or - wrong = "constanly" and right = "constantly" - or - wrong = "constarnation" and right = "consternation" - or - wrong = "constatn" and right = "constant" - or - wrong = "constinually" and right = "continually" - or - wrong = "constituant" and right = "constituent" - or - wrong = "constituants" and right = "constituents" - or - wrong = "constituion" and right = "constitution" - or - wrong = "constituional" and right = "constitutional" - or - wrong = "consttruction" and right = "construction" - or - wrong = "constuction" and right = "construction" - or - wrong = "consulant" and right = "consultant" - or - wrong = "consumate" and right = "consummate" - or - wrong = "consumated" and right = "consummated" - or - wrong = "contaiminate" and right = "contaminate" - or - wrong = "containes" and right = "contains" - or - wrong = "contamporaries" and right = "contemporaries" - or - wrong = "contamporary" and right = "contemporary" - or - wrong = "contempoary" and right = "contemporary" - or - wrong = "contemporaneus" and right = "contemporaneous" - or - wrong = "contempory" and right = "contemporary" - or - wrong = "contendor" and right = "contender" - or - wrong = "contian" and right = "contain" - or - wrong = "contians" and right = "contains" - or - wrong = "contibute" and right = "contribute" - or - wrong = "contibuted" and right = "contributed" - or - wrong = "contibutes" and right = "contributes" - or - wrong = "contigent" and right = "contingent" - or - wrong = "contined" and right = "continued" - or - wrong = "continential" and right = "continental" - or - wrong = "continous" and right = "continuous" - or - wrong = "continously" and right = "continuously" - or - wrong = "continueing" and right = "continuing" - or - wrong = "contravercial" and right = "controversial" - or - wrong = "contraversy" and right = "controversy" - or - wrong = "contributer" and right = "contributor" - or - wrong = "contributers" and right = "contributors" - or - wrong = "contritutions" and right = "contributions" - or - wrong = "controled" and right = "controlled" - or - wrong = "controling" and right = "controlling" - or - wrong = "controll" and right = "control" - or - wrong = "controlls" and right = "controls" - or - wrong = "controvercial" and right = "controversial" - or - wrong = "controvercy" and right = "controversy" - or - wrong = "controveries" and right = "controversies" - or - wrong = "controversal" and right = "controversial" - or - wrong = "controversey" and right = "controversy" - or - wrong = "controvertial" and right = "controversial" - or - wrong = "controvery" and right = "controversy" - or - wrong = "contruction" and right = "construction" - or - wrong = "contructor" and right = "constructor" - or - wrong = "contstruction" and right = "construction" - or - wrong = "conveinent" and right = "convenient" - or - wrong = "convenant" and right = "covenant" - or - wrong = "convential" and right = "conventional" - or - wrong = "convertables" and right = "convertibles" - or - wrong = "convertion" and right = "conversion" - or - wrong = "conviced" and right = "convinced" - or - wrong = "convienient" and right = "convenient" - or - wrong = "coordiantion" and right = "coordination" - or - wrong = "coorperation" and right = "cooperation" - or - wrong = "coorperation" and right = "corporation" - or - wrong = "coorperations" and right = "corporations" - or - wrong = "copmetitors" and right = "competitors" - or - wrong = "coputer" and right = "computer" - or - wrong = "copywrite" and right = "copyright" - or - wrong = "coridal" and right = "cordial" - or - wrong = "cornmitted" and right = "committed" - or - wrong = "corosion" and right = "corrosion" - or - wrong = "corparate" and right = "corporate" - or - wrong = "corperations" and right = "corporations" - or - wrong = "correcters" and right = "correctors" - or - wrong = "correponding" and right = "corresponding" - or - wrong = "correposding" and right = "corresponding" - or - wrong = "correspondant" and right = "correspondent" - or - wrong = "correspondants" and right = "correspondents" - or - wrong = "corridoors" and right = "corridors" - or - wrong = "corrispond" and right = "correspond" - or - wrong = "corrispondant" and right = "correspondent" - or - wrong = "corrispondants" and right = "correspondents" - or - wrong = "corrisponded" and right = "corresponded" - or - wrong = "corrisponding" and right = "corresponding" - or - wrong = "corrisponds" and right = "corresponds" - or - wrong = "costitution" and right = "constitution" - or - wrong = "coucil" and right = "council" - or - wrong = "coudl" and right = "cloud" - or - wrong = "coudl" and right = "could" - or - wrong = "councellor" and right = "councillor" - or - wrong = "councellor" and right = "councilor" - or - wrong = "councellor" and right = "counselor" - or - wrong = "councellors" and right = "councillors" - or - wrong = "councellors" and right = "councilors" - or - wrong = "councellors" and right = "counselors" - or - wrong = "counries" and right = "countries" - or - wrong = "countains" and right = "contains" - or - wrong = "countires" and right = "countries" - or - wrong = "coururier" and right = "courier" - or - wrong = "coururier" and right = "couturier" - or - wrong = "coverted" and right = "converted" - or - wrong = "coverted" and right = "covered" - or - wrong = "coverted" and right = "coveted" - or - wrong = "cpoy" and right = "copy" - or - wrong = "cpoy" and right = "coy" - or - wrong = "creaeted" and right = "created" - or - wrong = "creedence" and right = "credence" - or - wrong = "critereon" and right = "criterion" - or - wrong = "criterias" and right = "criteria" - or - wrong = "criticists" and right = "critics" - or - wrong = "critising" and right = "criticising" - or - wrong = "critising" and right = "criticizing" - or - wrong = "critisising" and right = "criticising" - or - wrong = "critisism" and right = "criticism" - or - wrong = "critisisms" and right = "criticisms" - or - wrong = "critisize" and right = "criticise" - or - wrong = "critisize" and right = "criticize" - or - wrong = "critisized" and right = "criticised" - or - wrong = "critisized" and right = "criticized" - or - wrong = "critisizes" and right = "criticises" - or - wrong = "critisizes" and right = "criticizes" - or - wrong = "critisizing" and right = "criticising" - or - wrong = "critisizing" and right = "criticizing" - or - wrong = "critized" and right = "criticized" - or - wrong = "critizing" and right = "criticizing" - or - wrong = "crockodiles" and right = "crocodiles" - or - wrong = "crowm" and right = "crown" - or - wrong = "crtical" and right = "critical" - or - wrong = "crticised" and right = "criticised" - or - wrong = "crucifiction" and right = "crucifixion" - or - wrong = "crusies" and right = "cruises" - or - wrong = "crutial" and right = "crucial" - or - wrong = "crystalisation" and right = "crystallisation" - or - wrong = "culiminating" and right = "culminating" - or - wrong = "cumulatative" and right = "cumulative" - or - wrong = "curch" and right = "church" - or - wrong = "curcuit" and right = "circuit" - or - wrong = "currenly" and right = "currently" - or - wrong = "curriculem" and right = "curriculum" - or - wrong = "cxan" and right = "cyan" - or - wrong = "cyclinder" and right = "cylinder" - or - wrong = "dacquiri" and right = "daiquiri" - or - wrong = "daed" and right = "dead" - or - wrong = "dael" and right = "dahl" - or - wrong = "dael" and right = "deal" - or - wrong = "dael" and right = "dial" - or - wrong = "dalmation" and right = "dalmatian" - or - wrong = "damenor" and right = "demeanor" - or - wrong = "dammage" and right = "damage" - or - wrong = "dardenelles" and right = "dardanelles" - or - wrong = "daugher" and right = "daughter" - or - wrong = "deafult" and right = "default" - or - wrong = "debateable" and right = "debatable" - or - wrong = "decendant" and right = "descendant" - or - wrong = "decendants" and right = "descendants" - or - wrong = "decendent" and right = "descendant" - or - wrong = "decendents" and right = "descendants" - or - wrong = "decideable" and right = "decidable" - or - wrong = "decidely" and right = "decidedly" - or - wrong = "decieved" and right = "deceived" - or - wrong = "decison" and right = "decision" - or - wrong = "decomissioned" and right = "decommissioned" - or - wrong = "decomposit" and right = "decompose" - or - wrong = "decomposited" and right = "decomposed" - or - wrong = "decompositing" and right = "decomposing" - or - wrong = "decomposits" and right = "decomposes" - or - wrong = "decress" and right = "decrees" - or - wrong = "decribe" and right = "describe" - or - wrong = "decribed" and right = "described" - or - wrong = "decribes" and right = "describes" - or - wrong = "decribing" and right = "describing" - or - wrong = "dectect" and right = "detect" - or - wrong = "defendent" and right = "defendant" - or - wrong = "defendents" and right = "defendants" - or - wrong = "deffensively" and right = "defensively" - or - wrong = "deffine" and right = "define" - or - wrong = "deffined" and right = "defined" - or - wrong = "definance" and right = "defiance" - or - wrong = "definate" and right = "definite" - or - wrong = "definately" and right = "definitely" - or - wrong = "definatly" and right = "definitely" - or - wrong = "definetly" and right = "definitely" - or - wrong = "definining" and right = "defining" - or - wrong = "definit" and right = "definite" - or - wrong = "definitly" and right = "definitely" - or - wrong = "definiton" and right = "definition" - or - wrong = "defintion" and right = "definition" - or - wrong = "defualt" and right = "default" - or - wrong = "defult" and right = "default" - or - wrong = "degrate" and right = "degrade" - or - wrong = "delagates" and right = "delegates" - or - wrong = "delapidated" and right = "dilapidated" - or - wrong = "delerious" and right = "delirious" - or - wrong = "delevopment" and right = "development" - or - wrong = "deliberatly" and right = "deliberately" - or - wrong = "delusionally" and right = "delusively" - or - wrong = "demenor" and right = "demeanor" - or - wrong = "demographical" and right = "demographic" - or - wrong = "demolision" and right = "demolition" - or - wrong = "demorcracy" and right = "democracy" - or - wrong = "demostration" and right = "demonstration" - or - wrong = "denegrating" and right = "denigrating" - or - wrong = "densly" and right = "densely" - or - wrong = "deparment" and right = "department" - or - wrong = "deparmental" and right = "departmental" - or - wrong = "deparments" and right = "departments" - or - wrong = "dependance" and right = "dependence" - or - wrong = "dependancy" and right = "dependency" - or - wrong = "deram" and right = "dram" - or - wrong = "deram" and right = "dream" - or - wrong = "deriviated" and right = "derived" - or - wrong = "derivitive" and right = "derivative" - or - wrong = "derogitory" and right = "derogatory" - or - wrong = "descendands" and right = "descendants" - or - wrong = "descibed" and right = "described" - or - wrong = "desciptors" and right = "descriptors" - or - wrong = "descision" and right = "decision" - or - wrong = "descisions" and right = "decisions" - or - wrong = "descriibes" and right = "describes" - or - wrong = "descripters" and right = "descriptors" - or - wrong = "descripton" and right = "description" - or - wrong = "desctruction" and right = "destruction" - or - wrong = "descuss" and right = "discuss" - or - wrong = "desgined" and right = "designed" - or - wrong = "deside" and right = "decide" - or - wrong = "desigining" and right = "designing" - or - wrong = "desinations" and right = "destinations" - or - wrong = "desintegrated" and right = "disintegrated" - or - wrong = "desintegration" and right = "disintegration" - or - wrong = "desireable" and right = "desirable" - or - wrong = "desitned" and right = "destined" - or - wrong = "desktiop" and right = "desktop" - or - wrong = "desorder" and right = "disorder" - or - wrong = "desoriented" and right = "disoriented" - or - wrong = "desparate" and right = "desperate" - or - wrong = "desparate" and right = "disparate" - or - wrong = "despict" and right = "depict" - or - wrong = "despiration" and right = "desperation" - or - wrong = "dessicated" and right = "desiccated" - or - wrong = "dessigned" and right = "designed" - or - wrong = "destablized" and right = "destabilized" - or - wrong = "destory" and right = "destroy" - or - wrong = "desugered" and right = "desugared" - or - wrong = "detailled" and right = "detailed" - or - wrong = "detatched" and right = "detached" - or - wrong = "deteoriated" and right = "deteriorated" - or - wrong = "deteriate" and right = "deteriorate" - or - wrong = "deterioriating" and right = "deteriorating" - or - wrong = "determinining" and right = "determining" - or - wrong = "detremental" and right = "detrimental" - or - wrong = "devasted" and right = "devastated" - or - wrong = "develope" and right = "develop" - or - wrong = "developement" and right = "development" - or - wrong = "developped" and right = "developed" - or - wrong = "develpment" and right = "development" - or - wrong = "devels" and right = "delves" - or - wrong = "devestated" and right = "devastated" - or - wrong = "devestating" and right = "devastating" - or - wrong = "devide" and right = "divide" - or - wrong = "devided" and right = "divided" - or - wrong = "devistating" and right = "devastating" - or - wrong = "devolopement" and right = "development" - or - wrong = "diablical" and right = "diabolical" - or - wrong = "diamons" and right = "diamonds" - or - wrong = "diaster" and right = "disaster" - or - wrong = "dichtomy" and right = "dichotomy" - or - wrong = "diconnects" and right = "disconnects" - or - wrong = "dicover" and right = "discover" - or - wrong = "dicovered" and right = "discovered" - or - wrong = "dicovering" and right = "discovering" - or - wrong = "dicovers" and right = "discovers" - or - wrong = "dicovery" and right = "discovery" - or - wrong = "dictionarys" and right = "dictionaries" - or - wrong = "dicussed" and right = "discussed" - or - wrong = "diea" and right = "die" - or - wrong = "diea" and right = "idea" - or - wrong = "dieing" and right = "dyeing" - or - wrong = "dieing" and right = "dying" - or - wrong = "dieties" and right = "deities" - or - wrong = "diety" and right = "deity" - or - wrong = "diferent" and right = "different" - or - wrong = "diferrent" and right = "different" - or - wrong = "differentiatiations" and right = "differentiations" - or - wrong = "differnt" and right = "different" - or - wrong = "difficulity" and right = "difficulty" - or - wrong = "diffrent" and right = "different" - or - wrong = "dificulties" and right = "difficulties" - or - wrong = "dificulty" and right = "difficulty" - or - wrong = "dimenions" and right = "dimensions" - or - wrong = "dimention" and right = "dimension" - or - wrong = "dimentional" and right = "dimensional" - or - wrong = "dimentions" and right = "dimensions" - or - wrong = "dimesnional" and right = "dimensional" - or - wrong = "diminuitive" and right = "diminutive" - or - wrong = "dimunitive" and right = "diminutive" - or - wrong = "diosese" and right = "diocese" - or - wrong = "diphtong" and right = "diphthong" - or - wrong = "diphtongs" and right = "diphthongs" - or - wrong = "diplomancy" and right = "diplomacy" - or - wrong = "dipthong" and right = "diphthong" - or - wrong = "dipthongs" and right = "diphthongs" - or - wrong = "directoty" and right = "directory" - or - wrong = "dirived" and right = "derived" - or - wrong = "disagreeed" and right = "disagreed" - or - wrong = "disapeared" and right = "disappeared" - or - wrong = "disapointing" and right = "disappointing" - or - wrong = "disappearred" and right = "disappeared" - or - wrong = "disaproval" and right = "disapproval" - or - wrong = "disasterous" and right = "disastrous" - or - wrong = "disatisfaction" and right = "dissatisfaction" - or - wrong = "disatisfied" and right = "dissatisfied" - or - wrong = "disatrous" and right = "disastrous" - or - wrong = "discontentment" and right = "discontent" - or - wrong = "discribe" and right = "describe" - or - wrong = "discribed" and right = "described" - or - wrong = "discribes" and right = "describes" - or - wrong = "discribing" and right = "describing" - or - wrong = "disctinction" and right = "distinction" - or - wrong = "disctinctive" and right = "distinctive" - or - wrong = "disemination" and right = "dissemination" - or - wrong = "disenchanged" and right = "disenchanted" - or - wrong = "disiplined" and right = "disciplined" - or - wrong = "disobediance" and right = "disobedience" - or - wrong = "disobediant" and right = "disobedient" - or - wrong = "disolved" and right = "dissolved" - or - wrong = "disover" and right = "discover" - or - wrong = "dispair" and right = "despair" - or - wrong = "disparingly" and right = "disparagingly" - or - wrong = "dispence" and right = "dispense" - or - wrong = "dispenced" and right = "dispensed" - or - wrong = "dispencing" and right = "dispensing" - or - wrong = "dispicable" and right = "despicable" - or - wrong = "dispite" and right = "despite" - or - wrong = "dispostion" and right = "disposition" - or - wrong = "disproportiate" and right = "disproportionate" - or - wrong = "disputandem" and right = "disputandum" - or - wrong = "disricts" and right = "districts" - or - wrong = "dissagreement" and right = "disagreement" - or - wrong = "dissapear" and right = "disappear" - or - wrong = "dissapearance" and right = "disappearance" - or - wrong = "dissapeared" and right = "disappeared" - or - wrong = "dissapearing" and right = "disappearing" - or - wrong = "dissapears" and right = "disappears" - or - wrong = "dissappear" and right = "disappear" - or - wrong = "dissappears" and right = "disappears" - or - wrong = "dissappointed" and right = "disappointed" - or - wrong = "dissarray" and right = "disarray" - or - wrong = "dissobediance" and right = "disobedience" - or - wrong = "dissobediant" and right = "disobedient" - or - wrong = "dissobedience" and right = "disobedience" - or - wrong = "dissobedient" and right = "disobedient" - or - wrong = "distiction" and right = "distinction" - or - wrong = "distingish" and right = "distinguish" - or - wrong = "distingished" and right = "distinguished" - or - wrong = "distingishes" and right = "distinguishes" - or - wrong = "distingishing" and right = "distinguishing" - or - wrong = "distingquished" and right = "distinguished" - or - wrong = "distrubution" and right = "distribution" - or - wrong = "distruction" and right = "destruction" - or - wrong = "distructive" and right = "destructive" - or - wrong = "ditributed" and right = "distributed" - or - wrong = "diversed" and right = "diverged" - or - wrong = "diversed" and right = "diverse" - or - wrong = "divice" and right = "device" - or - wrong = "divinition" and right = "divination" - or - wrong = "divison" and right = "division" - or - wrong = "divisons" and right = "divisions" - or - wrong = "doccument" and right = "document" - or - wrong = "doccumented" and right = "documented" - or - wrong = "doccuments" and right = "documents" - or - wrong = "docrines" and right = "doctrines" - or - wrong = "doctines" and right = "doctrines" - or - wrong = "documenatry" and right = "documentary" - or - wrong = "doens" and right = "does" - or - wrong = "doign" and right = "doing" - or - wrong = "dominaton" and right = "domination" - or - wrong = "dominent" and right = "dominant" - or - wrong = "dominiant" and right = "dominant" - or - wrong = "donig" and right = "doing" - or - wrong = "doub" and right = "daub" - or - wrong = "doub" and right = "doubt" - or - wrong = "doulbe" and right = "double" - or - wrong = "dowloads" and right = "downloads" - or - wrong = "dramtic" and right = "dramatic" - or - wrong = "draughtman" and right = "draughtsman" - or - wrong = "dravadian" and right = "dravidian" - or - wrong = "dreasm" and right = "dreams" - or - wrong = "driectly" and right = "directly" - or - wrong = "drnik" and right = "drink" - or - wrong = "druming" and right = "drumming" - or - wrong = "drummless" and right = "drumless" - or - wrong = "dum" and right = "dumb" - or - wrong = "dupicate" and right = "duplicate" - or - wrong = "durig" and right = "during" - or - wrong = "durring" and right = "during" - or - wrong = "duting" and right = "during" - or - wrong = "dyas" and right = "dryas" - or - wrong = "eahc" and right = "each" - or - wrong = "ealier" and right = "earlier" - or - wrong = "earlies" and right = "earliest" - or - wrong = "earnt" and right = "earned" - or - wrong = "ecclectic" and right = "eclectic" - or - wrong = "eceonomy" and right = "economy" - or - wrong = "ecidious" and right = "deciduous" - or - wrong = "eclispe" and right = "eclipse" - or - wrong = "ecomonic" and right = "economic" - or - wrong = "ect" and right = "etc" - or - wrong = "editting" and right = "editing" - or - wrong = "eearly" and right = "early" - or - wrong = "efel" and right = "evil" - or - wrong = "effeciency" and right = "efficiency" - or - wrong = "effecient" and right = "efficient" - or - wrong = "effeciently" and right = "efficiently" - or - wrong = "efficency" and right = "efficiency" - or - wrong = "efficent" and right = "efficient" - or - wrong = "efficently" and right = "efficiently" - or - wrong = "efford" and right = "afford" - or - wrong = "efford" and right = "effort" - or - wrong = "effords" and right = "affords" - or - wrong = "effords" and right = "efforts" - or - wrong = "effulence" and right = "effluence" - or - wrong = "eigth" and right = "eight" - or - wrong = "eigth" and right = "eighth" - or - wrong = "eiter" and right = "either" - or - wrong = "elction" and right = "election" - or - wrong = "electic" and right = "eclectic" - or - wrong = "electic" and right = "electric" - or - wrong = "electon" and right = "election" - or - wrong = "electon" and right = "electron" - or - wrong = "electrial" and right = "electrical" - or - wrong = "electricly" and right = "electrically" - or - wrong = "electricty" and right = "electricity" - or - wrong = "elementay" and right = "elementary" - or - wrong = "eleminated" and right = "eliminated" - or - wrong = "eleminating" and right = "eliminating" - or - wrong = "eles" and right = "eels" - or - wrong = "eletricity" and right = "electricity" - or - wrong = "elicided" and right = "elicited" - or - wrong = "eligable" and right = "eligible" - or - wrong = "elimentary" and right = "elementary" - or - wrong = "ellected" and right = "elected" - or - wrong = "elphant" and right = "elephant" - or - wrong = "embarass" and right = "embarrass" - or - wrong = "embarassed" and right = "embarrassed" - or - wrong = "embarassing" and right = "embarrassing" - or - wrong = "embarassment" and right = "embarrassment" - or - wrong = "embargos" and right = "embargoes" - or - wrong = "embarras" and right = "embarrass" - or - wrong = "embarrased" and right = "embarrassed" - or - wrong = "embarrasing" and right = "embarrassing" - or - wrong = "embarrasment" and right = "embarrassment" - or - wrong = "embezelled" and right = "embezzled" - or - wrong = "emblamatic" and right = "emblematic" - or - wrong = "eminate" and right = "emanate" - or - wrong = "eminated" and right = "emanated" - or - wrong = "emision" and right = "emission" - or - wrong = "emited" and right = "emitted" - or - wrong = "emiting" and right = "emitting" - or - wrong = "emition" and right = "emission" - or - wrong = "emition" and right = "emotion" - or - wrong = "emmediately" and right = "immediately" - or - wrong = "emmigrated" and right = "emigrated" - or - wrong = "emmigrated" and right = "immigrated" - or - wrong = "emminent" and right = "eminent" - or - wrong = "emminent" and right = "imminent" - or - wrong = "emminently" and right = "eminently" - or - wrong = "emmisaries" and right = "emissaries" - or - wrong = "emmisarries" and right = "emissaries" - or - wrong = "emmisarry" and right = "emissary" - or - wrong = "emmisary" and right = "emissary" - or - wrong = "emmision" and right = "emission" - or - wrong = "emmisions" and right = "emissions" - or - wrong = "emmited" and right = "emitted" - or - wrong = "emmiting" and right = "emitting" - or - wrong = "emmitted" and right = "emitted" - or - wrong = "emmitting" and right = "emitting" - or - wrong = "emnity" and right = "enmity" - or - wrong = "emperical" and right = "empirical" - or - wrong = "emphaised" and right = "emphasised" - or - wrong = "emphsis" and right = "emphasis" - or - wrong = "emphysyma" and right = "emphysema" - or - wrong = "empirial" and right = "empirical" - or - wrong = "empirial" and right = "imperial" - or - wrong = "emporer" and right = "emperor" - or - wrong = "emprisoned" and right = "imprisoned" - or - wrong = "enameld" and right = "enameled" - or - wrong = "enchancement" and right = "enhancement" - or - wrong = "encouraing" and right = "encouraging" - or - wrong = "encryptiion" and right = "encryption" - or - wrong = "encylopedia" and right = "encyclopedia" - or - wrong = "endevors" and right = "endeavors" - or - wrong = "endevour" and right = "endeavour" - or - wrong = "endig" and right = "ending" - or - wrong = "endolithes" and right = "endoliths" - or - wrong = "enduce" and right = "induce" - or - wrong = "ened" and right = "need" - or - wrong = "enforceing" and right = "enforcing" - or - wrong = "engagment" and right = "engagement" - or - wrong = "engeneer" and right = "engineer" - or - wrong = "engeneering" and right = "engineering" - or - wrong = "engieneer" and right = "engineer" - or - wrong = "engieneers" and right = "engineers" - or - wrong = "enlargment" and right = "enlargement" - or - wrong = "enlargments" and right = "enlargements" - or - wrong = "enlish" and right = "english" - or - wrong = "enlish" and right = "enlist" - or - wrong = "enourmous" and right = "enormous" - or - wrong = "enourmously" and right = "enormously" - or - wrong = "ensconsed" and right = "ensconced" - or - wrong = "entaglements" and right = "entanglements" - or - wrong = "enteratinment" and right = "entertainment" - or - wrong = "enthusiatic" and right = "enthusiastic" - or - wrong = "entitity" and right = "entity" - or - wrong = "entitlied" and right = "entitled" - or - wrong = "entrepeneur" and right = "entrepreneur" - or - wrong = "entrepeneurs" and right = "entrepreneurs" - or - wrong = "enviorment" and right = "environment" - or - wrong = "enviormental" and right = "environmental" - or - wrong = "enviormentally" and right = "environmentally" - or - wrong = "enviorments" and right = "environments" - or - wrong = "enviornment" and right = "environment" - or - wrong = "enviornmental" and right = "environmental" - or - wrong = "enviornmentalist" and right = "environmentalist" - or - wrong = "enviornmentally" and right = "environmentally" - or - wrong = "enviornments" and right = "environments" - or - wrong = "enviroment" and right = "environment" - or - wrong = "enviromental" and right = "environmental" - or - wrong = "enviromentalist" and right = "environmentalist" - or - wrong = "enviromentally" and right = "environmentally" - or - wrong = "enviroments" and right = "environments" - or - wrong = "environemnt" and right = "environment" - or - wrong = "envolutionary" and right = "evolutionary" - or - wrong = "envrionments" and right = "environments" - or - wrong = "enxt" and right = "next" - or - wrong = "epidsodes" and right = "episodes" - or - wrong = "epsiode" and right = "episode" - or - wrong = "equialent" and right = "equivalent" - or - wrong = "equilibium" and right = "equilibrium" - or - wrong = "equilibrum" and right = "equilibrium" - or - wrong = "equiped" and right = "equipped" - or - wrong = "equippment" and right = "equipment" - or - wrong = "equitorial" and right = "equatorial" - or - wrong = "equivelant" and right = "equivalent" - or - wrong = "equivelent" and right = "equivalent" - or - wrong = "equivilant" and right = "equivalent" - or - wrong = "equivilent" and right = "equivalent" - or - wrong = "equivlalent" and right = "equivalent" - or - wrong = "erally" and right = "orally" - or - wrong = "erally" and right = "really" - or - wrong = "eratic" and right = "erratic" - or - wrong = "eratically" and right = "erratically" - or - wrong = "eraticly" and right = "erratically" - or - wrong = "erested" and right = "arrested" - or - wrong = "erested" and right = "erected" - or - wrong = "errupted" and right = "erupted" - or - wrong = "esential" and right = "essential" - or - wrong = "esitmated" and right = "estimated" - or - wrong = "esle" and right = "else" - or - wrong = "especialy" and right = "especially" - or - wrong = "essencial" and right = "essential" - or - wrong = "essense" and right = "essence" - or - wrong = "essentail" and right = "essential" - or - wrong = "essentialy" and right = "essentially" - or - wrong = "essentual" and right = "essential" - or - wrong = "essesital" and right = "essential" - or - wrong = "estabishes" and right = "establishes" - or - wrong = "establising" and right = "establishing" - or - wrong = "ethnocentricm" and right = "ethnocentrism" - or - wrong = "ethose" and right = "ethos" - or - wrong = "ethose" and right = "those" - or - wrong = "europian" and right = "european" - or - wrong = "europians" and right = "europeans" - or - wrong = "eurpean" and right = "european" - or - wrong = "eurpoean" and right = "european" - or - wrong = "evenhtually" and right = "eventually" - or - wrong = "eventally" and right = "eventually" - or - wrong = "eventially" and right = "eventually" - or - wrong = "eventualy" and right = "eventually" - or - wrong = "everthing" and right = "everything" - or - wrong = "everyting" and right = "everything" - or - wrong = "eveyr" and right = "every" - or - wrong = "evidentally" and right = "evidently" - or - wrong = "exagerate" and right = "exaggerate" - or - wrong = "exagerated" and right = "exaggerated" - or - wrong = "exagerates" and right = "exaggerates" - or - wrong = "exagerating" and right = "exaggerating" - or - wrong = "exagerrate" and right = "exaggerate" - or - wrong = "exagerrated" and right = "exaggerated" - or - wrong = "exagerrates" and right = "exaggerates" - or - wrong = "exagerrating" and right = "exaggerating" - or - wrong = "examinated" and right = "examined" - or - wrong = "exampt" and right = "exempt" - or - wrong = "exapansion" and right = "expansion" - or - wrong = "excact" and right = "exact" - or - wrong = "excange" and right = "exchange" - or - wrong = "excecute" and right = "execute" - or - wrong = "excecuted" and right = "executed" - or - wrong = "excecutes" and right = "executes" - or - wrong = "excecuting" and right = "executing" - or - wrong = "excecution" and right = "execution" - or - wrong = "excedded" and right = "exceeded" - or - wrong = "excelent" and right = "excellent" - or - wrong = "excell" and right = "excel" - or - wrong = "excellance" and right = "excellence" - or - wrong = "excellant" and right = "excellent" - or - wrong = "excells" and right = "excels" - or - wrong = "excercise" and right = "exercise" - or - wrong = "exchanching" and right = "exchanging" - or - wrong = "excisted" and right = "existed" - or - wrong = "exculsivly" and right = "exclusively" - or - wrong = "execising" and right = "exercising" - or - wrong = "exection" and right = "execution" - or - wrong = "exectued" and right = "executed" - or - wrong = "exeedingly" and right = "exceedingly" - or - wrong = "exelent" and right = "excellent" - or - wrong = "exellent" and right = "excellent" - or - wrong = "exemple" and right = "example" - or - wrong = "exept" and right = "except" - or - wrong = "exeptional" and right = "exceptional" - or - wrong = "exerbate" and right = "exacerbate" - or - wrong = "exerbated" and right = "exacerbated" - or - wrong = "exerciese" and right = "exercises" - or - wrong = "exerpt" and right = "excerpt" - or - wrong = "exerpts" and right = "excerpts" - or - wrong = "exersize" and right = "exercise" - or - wrong = "exerternal" and right = "external" - or - wrong = "exhalted" and right = "exalted" - or - wrong = "exhibtion" and right = "exhibition" - or - wrong = "exibition" and right = "exhibition" - or - wrong = "exibitions" and right = "exhibitions" - or - wrong = "exicting" and right = "exciting" - or - wrong = "exinct" and right = "extinct" - or - wrong = "existance" and right = "existence" - or - wrong = "existant" and right = "existent" - or - wrong = "existince" and right = "existence" - or - wrong = "exliled" and right = "exiled" - or - wrong = "exludes" and right = "excludes" - or - wrong = "exmaple" and right = "example" - or - wrong = "exonorate" and right = "exonerate" - or - wrong = "exoskelaton" and right = "exoskeleton" - or - wrong = "expalin" and right = "explain" - or - wrong = "expatriot" and right = "expatriate" - or - wrong = "expeced" and right = "expected" - or - wrong = "expecially" and right = "especially" - or - wrong = "expeditonary" and right = "expeditionary" - or - wrong = "expeiments" and right = "experiments" - or - wrong = "expell" and right = "expel" - or - wrong = "expells" and right = "expels" - or - wrong = "experiance" and right = "experience" - or - wrong = "experianced" and right = "experienced" - or - wrong = "expession" and right = "expression" - or - wrong = "expessions" and right = "expressions" - or - wrong = "expiditions" and right = "expeditions" - or - wrong = "expierence" and right = "experience" - or - wrong = "explaination" and right = "explanation" - or - wrong = "explaning" and right = "explaining" - or - wrong = "explictly" and right = "explicitly" - or - wrong = "exploititive" and right = "exploitative" - or - wrong = "explotation" and right = "exploitation" - or - wrong = "expropiated" and right = "expropriated" - or - wrong = "expropiation" and right = "expropriation" - or - wrong = "exressed" and right = "expressed" - or - wrong = "extemely" and right = "extremely" - or - wrong = "extened" and right = "extended" - or - wrong = "extention" and right = "extension" - or - wrong = "extentions" and right = "extensions" - or - wrong = "extered" and right = "exerted" - or - wrong = "extermist" and right = "extremist" - or - wrong = "extint" and right = "extant" - or - wrong = "extint" and right = "extinct" - or - wrong = "extracter" and right = "extractor" - or - wrong = "extradiction" and right = "extradition" - or - wrong = "extraterrestial" and right = "extraterrestrial" - or - wrong = "extraterrestials" and right = "extraterrestrials" - or - wrong = "extravagent" and right = "extravagant" - or - wrong = "extrememly" and right = "extremely" - or - wrong = "extremeophile" and right = "extremophile" - or - wrong = "extremly" and right = "extremely" - or - wrong = "extrordinarily" and right = "extraordinarily" - or - wrong = "extrordinary" and right = "extraordinary" - or - wrong = "eyar" and right = "eyas" - or - wrong = "eyar" and right = "year" - or - wrong = "eyars" and right = "eyas" - or - wrong = "eyars" and right = "years" - or - wrong = "eyasr" and right = "eyas" - or - wrong = "eyasr" and right = "years" - or - wrong = "faciliate" and right = "facilitate" - or - wrong = "faciliated" and right = "facilitated" - or - wrong = "faciliates" and right = "facilitates" - or - wrong = "facilites" and right = "facilities" - or - wrong = "facillitate" and right = "facilitate" - or - wrong = "facinated" and right = "fascinated" - or - wrong = "facist" and right = "fascist" - or - wrong = "familes" and right = "families" - or - wrong = "familliar" and right = "familiar" - or - wrong = "famoust" and right = "famous" - or - wrong = "fanatism" and right = "fanaticism" - or - wrong = "farenheit" and right = "fahrenheit" - or - wrong = "fatc" and right = "fact" - or - wrong = "faught" and right = "fought" - or - wrong = "favoutrable" and right = "favourable" - or - wrong = "feasable" and right = "feasible" - or - wrong = "febuary" and right = "february" - or - wrong = "feburary" and right = "february" - or - wrong = "fedreally" and right = "federally" - or - wrong = "femminist" and right = "feminist" - or - wrong = "feromone" and right = "pheromone" - or - wrong = "fertily" and right = "fertility" - or - wrong = "fianite" and right = "finite" - or - wrong = "fianlly" and right = "finally" - or - wrong = "ficticious" and right = "fictitious" - or - wrong = "fictious" and right = "fictitious" - or - wrong = "fidn" and right = "find" - or - wrong = "fiel" and right = "feel" - or - wrong = "fiel" and right = "field" - or - wrong = "fiel" and right = "file" - or - wrong = "fiel" and right = "phial" - or - wrong = "fiels" and right = "feels" - or - wrong = "fiels" and right = "fields" - or - wrong = "fiels" and right = "files" - or - wrong = "fiels" and right = "phials" - or - wrong = "fiercly" and right = "fiercely" - or - wrong = "fightings" and right = "fighting" - or - wrong = "filiament" and right = "filament" - or - wrong = "fimilies" and right = "families" - or - wrong = "finacial" and right = "financial" - or - wrong = "finaly" and right = "finally" - or - wrong = "financialy" and right = "financially" - or - wrong = "firends" and right = "friends" - or - wrong = "firts" and right = "first" - or - wrong = "firts" and right = "flirts" - or - wrong = "fisionable" and right = "fissionable" - or - wrong = "flamable" and right = "flammable" - or - wrong = "flawess" and right = "flawless" - or - wrong = "fleed" and right = "fled" - or - wrong = "fleed" and right = "freed" - or - wrong = "flemmish" and right = "flemish" - or - wrong = "florescent" and right = "fluorescent" - or - wrong = "flourescent" and right = "fluorescent" - or - wrong = "flourine" and right = "fluorine" - or - wrong = "flourishment" and right = "flourishing" - or - wrong = "fluorish" and right = "flourish" - or - wrong = "follwoing" and right = "following" - or - wrong = "folowing" and right = "following" - or - wrong = "fomed" and right = "formed" - or - wrong = "fomr" and right = "form" - or - wrong = "fomr" and right = "from" - or - wrong = "fonetic" and right = "phonetic" - or - wrong = "fontrier" and right = "fontier" - or - wrong = "foootball" and right = "football" - or - wrong = "forbad" and right = "forbade" - or - wrong = "forbiden" and right = "forbidden" - or - wrong = "foreward" and right = "foreword" - or - wrong = "forfiet" and right = "forfeit" - or - wrong = "forhead" and right = "forehead" - or - wrong = "foriegn" and right = "foreign" - or - wrong = "formalhaut" and right = "fomalhaut" - or - wrong = "formallize" and right = "formalize" - or - wrong = "formallized" and right = "formalized" - or - wrong = "formaly" and right = "formally" - or - wrong = "formaly" and right = "formerly" - or - wrong = "formelly" and right = "formerly" - or - wrong = "formidible" and right = "formidable" - or - wrong = "formost" and right = "foremost" - or - wrong = "forsaw" and right = "foresaw" - or - wrong = "forseeable" and right = "foreseeable" - or - wrong = "fortelling" and right = "foretelling" - or - wrong = "forunner" and right = "forerunner" - or - wrong = "foucs" and right = "focus" - or - wrong = "foudn" and right = "found" - or - wrong = "fougth" and right = "fought" - or - wrong = "foundaries" and right = "foundries" - or - wrong = "foundary" and right = "foundry" - or - wrong = "foundland" and right = "newfoundland" - or - wrong = "fourties" and right = "forties" - or - wrong = "fourty" and right = "forty" - or - wrong = "fouth" and right = "fourth" - or - wrong = "foward" and right = "forward" - or - wrong = "framwork" and right = "framework" - or - wrong = "fransiscan" and right = "franciscan" - or - wrong = "fransiscans" and right = "franciscans" - or - wrong = "freind" and right = "friend" - or - wrong = "freindly" and right = "friendly" - or - wrong = "frequentily" and right = "frequently" - or - wrong = "frome" and right = "from" - or - wrong = "fromed" and right = "formed" - or - wrong = "froniter" and right = "frontier" - or - wrong = "fucntion" and right = "function" - or - wrong = "fucntioning" and right = "functioning" - or - wrong = "fufill" and right = "fulfill" - or - wrong = "fufilled" and right = "fulfilled" - or - wrong = "fulfiled" and right = "fulfilled" - or - wrong = "fullfill" and right = "fulfill" - or - wrong = "fullfilled" and right = "fulfilled" - or - wrong = "funcion" and right = "function" - or - wrong = "fundametal" and right = "fundamental" - or - wrong = "fundametals" and right = "fundamentals" - or - wrong = "funguses" and right = "fungi" - or - wrong = "funtion" and right = "function" - or - wrong = "funtions" and right = "functions" - or - wrong = "furuther" and right = "further" - or - wrong = "futher" and right = "further" - or - wrong = "futhermore" and right = "furthermore" - or - wrong = "futhroc" and right = "futhark" - or - wrong = "futhroc" and right = "futhorc" - or - wrong = "gae" and right = "gael" - or - wrong = "gae" and right = "gale" - or - wrong = "gae" and right = "game" - or - wrong = "galatic" and right = "galactic" - or - wrong = "galations" and right = "galatians" - or - wrong = "gallaxies" and right = "galaxies" - or - wrong = "galvinized" and right = "galvanized" - or - wrong = "ganerate" and right = "generate" - or - wrong = "ganes" and right = "games" - or - wrong = "ganster" and right = "gangster" - or - wrong = "garantee" and right = "guarantee" - or - wrong = "garanteed" and right = "guaranteed" - or - wrong = "garantees" and right = "guarantees" - or - wrong = "garnison" and right = "garrison" - or - wrong = "gaurantee" and right = "guarantee" - or - wrong = "gauranteed" and right = "guaranteed" - or - wrong = "gaurantees" and right = "guarantees" - or - wrong = "gaurd" and right = "gourd" - or - wrong = "gaurd" and right = "guard" - or - wrong = "gaurentee" and right = "guarantee" - or - wrong = "gaurenteed" and right = "guaranteed" - or - wrong = "gaurentees" and right = "guarantees" - or - wrong = "geneological" and right = "genealogical" - or - wrong = "geneologies" and right = "genealogies" - or - wrong = "geneology" and right = "genealogy" - or - wrong = "generaly" and right = "generally" - or - wrong = "generatting" and right = "generating" - or - wrong = "genialia" and right = "genitalia" - or - wrong = "geographicial" and right = "geographical" - or - wrong = "geometrician" and right = "geometer" - or - wrong = "geometricians" and right = "geometers" - or - wrong = "gerat" and right = "great" - or - wrong = "ghandi" and right = "gandhi" - or - wrong = "glamourous" and right = "glamorous" - or - wrong = "glight" and right = "flight" - or - wrong = "gnawwed" and right = "gnawed" - or - wrong = "godess" and right = "goddess" - or - wrong = "godesses" and right = "goddesses" - or - wrong = "godounov" and right = "godunov" - or - wrong = "gogin" and right = "gauguin" - or - wrong = "gogin" and right = "going" - or - wrong = "goign" and right = "going" - or - wrong = "gonig" and right = "going" - or - wrong = "gothenberg" and right = "gothenburg" - or - wrong = "gottleib" and right = "gottlieb" - or - wrong = "gouvener" and right = "governor" - or - wrong = "govement" and right = "government" - or - wrong = "govenment" and right = "government" - or - wrong = "govenrment" and right = "government" - or - wrong = "goverance" and right = "governance" - or - wrong = "goverment" and right = "government" - or - wrong = "govermental" and right = "governmental" - or - wrong = "governer" and right = "governor" - or - wrong = "governmnet" and right = "government" - or - wrong = "govorment" and right = "government" - or - wrong = "govormental" and right = "governmental" - or - wrong = "govornment" and right = "government" - or - wrong = "gracefull" and right = "graceful" - or - wrong = "graet" and right = "great" - or - wrong = "grafitti" and right = "graffiti" - or - wrong = "gramatically" and right = "grammatically" - or - wrong = "grammaticaly" and right = "grammatically" - or - wrong = "grammer" and right = "grammar" - or - wrong = "grat" and right = "great" - or - wrong = "gratuitious" and right = "gratuitous" - or - wrong = "greatful" and right = "grateful" - or - wrong = "greatfully" and right = "gratefully" - or - wrong = "greif" and right = "grief" - or - wrong = "gridles" and right = "griddles" - or - wrong = "gropu" and right = "group" - or - wrong = "grwo" and right = "grow" - or - wrong = "guaduloupe" and right = "guadalupe" - or - wrong = "guaduloupe" and right = "guadeloupe" - or - wrong = "guadulupe" and right = "guadalupe" - or - wrong = "guadulupe" and right = "guadeloupe" - or - wrong = "guage" and right = "gauge" - or - wrong = "guarentee" and right = "guarantee" - or - wrong = "guarenteed" and right = "guaranteed" - or - wrong = "guarentees" and right = "guarantees" - or - wrong = "guatamala" and right = "guatemala" - or - wrong = "guatamalan" and right = "guatemalan" - or - wrong = "guerrila" and right = "guerrilla" - or - wrong = "guerrilas" and right = "guerrillas" - or - wrong = "guidence" and right = "guidance" - or - wrong = "guilia" and right = "giulia" - or - wrong = "guilio" and right = "giulio" - or - wrong = "guiness" and right = "guinness" - or - wrong = "guiseppe" and right = "giuseppe" - or - wrong = "gunanine" and right = "guanine" - or - wrong = "gurantee" and right = "guarantee" - or - wrong = "guranteed" and right = "guaranteed" - or - wrong = "gurantees" and right = "guarantees" - or - wrong = "guttaral" and right = "guttural" - or - wrong = "gutteral" and right = "guttural" - or - wrong = "habaeus" and right = "habeas" - or - wrong = "habeus" and right = "habeas" - or - wrong = "habsbourg" and right = "habsburg" - or - wrong = "haemorrage" and right = "haemorrhage" - or - wrong = "haev" and right = "have" - or - wrong = "haev" and right = "heave" - or - wrong = "halarious" and right = "hilarious" - or - wrong = "hallowean" and right = "halloween" - or - wrong = "halp" and right = "help" - or - wrong = "hander" and right = "handler" - or - wrong = "hapen" and right = "happen" - or - wrong = "hapened" and right = "happened" - or - wrong = "hapening" and right = "happening" - or - wrong = "happend" and right = "happened" - or - wrong = "happended" and right = "happened" - or - wrong = "happenned" and right = "happened" - or - wrong = "harased" and right = "harassed" - or - wrong = "harases" and right = "harasses" - or - wrong = "harasment" and right = "harassment" - or - wrong = "harasments" and right = "harassments" - or - wrong = "harassement" and right = "harassment" - or - wrong = "harras" and right = "harass" - or - wrong = "harrased" and right = "harassed" - or - wrong = "harrases" and right = "harasses" - or - wrong = "harrasing" and right = "harassing" - or - wrong = "harrasment" and right = "harassment" - or - wrong = "harrasments" and right = "harassments" - or - wrong = "harrassed" and right = "harassed" - or - wrong = "harrasses" and right = "harassed" - or - wrong = "harrassing" and right = "harassing" - or - wrong = "harrassment" and right = "harassment" - or - wrong = "harrassments" and right = "harassments" - or - wrong = "hatian" and right = "haitian" - or - wrong = "haviest" and right = "heaviest" - or - wrong = "headquarer" and right = "headquarter" - or - wrong = "headquater" and right = "headquarter" - or - wrong = "headquatered" and right = "headquartered" - or - wrong = "headquaters" and right = "headquarters" - or - wrong = "healthercare" and right = "healthcare" - or - wrong = "heared" and right = "heard" - or - wrong = "heathy" and right = "healthy" - or - wrong = "heidelburg" and right = "heidelberg" - or - wrong = "heigher" and right = "higher" - or - wrong = "heirarchy" and right = "hierarchy" - or - wrong = "heiroglyphics" and right = "hieroglyphics" - or - wrong = "helment" and right = "helmet" - or - wrong = "helpfull" and right = "helpful" - or - wrong = "helpped" and right = "helped" - or - wrong = "hemmorhage" and right = "hemorrhage" - or - wrong = "herad" and right = "heard" - or - wrong = "herad" and right = "hera" - or - wrong = "heridity" and right = "heredity" - or - wrong = "heroe" and right = "hero" - or - wrong = "heros" and right = "heroes" - or - wrong = "hertiage" and right = "heritage" - or - wrong = "hertzs" and right = "hertz" - or - wrong = "hesistant" and right = "hesitant" - or - wrong = "heterogenous" and right = "heterogeneous" - or - wrong = "hieght" and right = "height" - or - wrong = "hierachical" and right = "hierarchical" - or - wrong = "hierachies" and right = "hierarchies" - or - wrong = "hierachy" and right = "hierarchy" - or - wrong = "hierarcical" and right = "hierarchical" - or - wrong = "hierarcy" and right = "hierarchy" - or - wrong = "hieroglph" and right = "hieroglyph" - or - wrong = "hieroglphs" and right = "hieroglyphs" - or - wrong = "higer" and right = "higher" - or - wrong = "higest" and right = "highest" - or - wrong = "higway" and right = "highway" - or - wrong = "hillarious" and right = "hilarious" - or - wrong = "himselv" and right = "himself" - or - wrong = "hinderance" and right = "hindrance" - or - wrong = "hinderence" and right = "hindrance" - or - wrong = "hindrence" and right = "hindrance" - or - wrong = "hipopotamus" and right = "hippopotamus" - or - wrong = "hismelf" and right = "himself" - or - wrong = "histocompatability" and right = "histocompatibility" - or - wrong = "historicians" and right = "historians" - or - wrong = "holf" and right = "hold" - or - wrong = "holliday" and right = "holiday" - or - wrong = "homogeneize" and right = "homogenize" - or - wrong = "homogeneized" and right = "homogenized" - or - wrong = "honory" and right = "honorary" - or - wrong = "horrifing" and right = "horrifying" - or - wrong = "hosited" and right = "hoisted" - or - wrong = "hospitible" and right = "hospitable" - or - wrong = "hounour" and right = "honour" - or - wrong = "housr" and right = "hours" - or - wrong = "housr" and right = "house" - or - wrong = "howver" and right = "however" - or - wrong = "hsitorians" and right = "historians" - or - wrong = "hstory" and right = "history" - or - wrong = "hten" and right = "hen" - or - wrong = "hten" and right = "the" - or - wrong = "hten" and right = "then" - or - wrong = "htere" and right = "here" - or - wrong = "htere" and right = "there" - or - wrong = "htey" and right = "they" - or - wrong = "htikn" and right = "think" - or - wrong = "hting" and right = "thing" - or - wrong = "htink" and right = "think" - or - wrong = "htis" and right = "this" - or - wrong = "humer" and right = "humor" - or - wrong = "humer" and right = "humour" - or - wrong = "humerous" and right = "humerus" - or - wrong = "humerous" and right = "humorous" - or - wrong = "huminoid" and right = "humanoid" - or - wrong = "humoural" and right = "humoral" - or - wrong = "humurous" and right = "humorous" - or - wrong = "husban" and right = "husband" - or - wrong = "hvae" and right = "have" - or - wrong = "hvaing" and right = "having" - or - wrong = "hvea" and right = "have" - or - wrong = "hvea" and right = "heave" - or - wrong = "hwihc" and right = "which" - or - wrong = "hwile" and right = "while" - or - wrong = "hwole" and right = "whole" - or - wrong = "hydogen" and right = "hydrogen" - or - wrong = "hydropile" and right = "hydrophile" - or - wrong = "hydropilic" and right = "hydrophilic" - or - wrong = "hydropobe" and right = "hydrophobe" - or - wrong = "hydropobic" and right = "hydrophobic" - or - wrong = "hygeine" and right = "hygiene" - or - wrong = "hyjack" and right = "hijack" - or - wrong = "hyjacking" and right = "hijacking" - or - wrong = "hypocracy" and right = "hypocrisy" - or - wrong = "hypocrasy" and right = "hypocrisy" - or - wrong = "hypocricy" and right = "hypocrisy" - or - wrong = "hypocrit" and right = "hypocrite" - or - wrong = "hypocrits" and right = "hypocrites" - or - wrong = "iconclastic" and right = "iconoclastic" - or - wrong = "idaeidae" and right = "idea" - or - wrong = "idaes" and right = "ideas" - or - wrong = "idealogies" and right = "ideologies" - or - wrong = "idealogy" and right = "ideology" - or - wrong = "identicial" and right = "identical" - or - wrong = "identifers" and right = "identifiers" - or - wrong = "ideosyncratic" and right = "idiosyncratic" - or - wrong = "idesa" and right = "ideas" - or - wrong = "idesa" and right = "ides" - or - wrong = "idiosyncracy" and right = "idiosyncrasy" - or - wrong = "ihaca" and right = "ithaca" - or - wrong = "illegimacy" and right = "illegitimacy" - or - wrong = "illegitmate" and right = "illegitimate" - or - wrong = "illess" and right = "illness" - or - wrong = "illiegal" and right = "illegal" - or - wrong = "illution" and right = "illusion" - or - wrong = "ilness" and right = "illness" - or - wrong = "ilogical" and right = "illogical" - or - wrong = "imagenary" and right = "imaginary" - or - wrong = "imagin" and right = "imagine" - or - wrong = "imaginery" and right = "imagery" - or - wrong = "imaginery" and right = "imaginary" - or - wrong = "imanent" and right = "eminent" - or - wrong = "imanent" and right = "imminent" - or - wrong = "imcomplete" and right = "incomplete" - or - wrong = "imediately" and right = "immediately" - or - wrong = "imense" and right = "immense" - or - wrong = "imigrant" and right = "emigrant" - or - wrong = "imigrant" and right = "immigrant" - or - wrong = "imigrated" and right = "emigrated" - or - wrong = "imigrated" and right = "immigrated" - or - wrong = "imigration" and right = "emigration" - or - wrong = "imigration" and right = "immigration" - or - wrong = "iminent" and right = "eminent" - or - wrong = "iminent" and right = "immanent" - or - wrong = "iminent" and right = "imminent" - or - wrong = "immediatley" and right = "immediately" - or - wrong = "immediatly" and right = "immediately" - or - wrong = "immidately" and right = "immediately" - or - wrong = "immidiately" and right = "immediately" - or - wrong = "immitate" and right = "imitate" - or - wrong = "immitated" and right = "imitated" - or - wrong = "immitating" and right = "imitating" - or - wrong = "immitator" and right = "imitator" - or - wrong = "immunosupressant" and right = "immunosuppressant" - or - wrong = "impecabbly" and right = "impeccably" - or - wrong = "impedence" and right = "impedance" - or - wrong = "implamenting" and right = "implementing" - or - wrong = "impliment" and right = "implement" - or - wrong = "implimented" and right = "implemented" - or - wrong = "imploys" and right = "employs" - or - wrong = "importamt" and right = "important" - or - wrong = "impressario" and right = "impresario" - or - wrong = "imprioned" and right = "imprisoned" - or - wrong = "imprisonned" and right = "imprisoned" - or - wrong = "improvision" and right = "improvisation" - or - wrong = "improvments" and right = "improvements" - or - wrong = "inablility" and right = "inability" - or - wrong = "inaccessable" and right = "inaccessible" - or - wrong = "inadiquate" and right = "inadequate" - or - wrong = "inadquate" and right = "inadequate" - or - wrong = "inadvertant" and right = "inadvertent" - or - wrong = "inadvertantly" and right = "inadvertently" - or - wrong = "inagurated" and right = "inaugurated" - or - wrong = "inaguration" and right = "inauguration" - or - wrong = "inappropiate" and right = "inappropriate" - or - wrong = "inaugures" and right = "inaugurates" - or - wrong = "inbalance" and right = "imbalance" - or - wrong = "inbalanced" and right = "imbalanced" - or - wrong = "inbetween" and right = "between" - or - wrong = "incarcirated" and right = "incarcerated" - or - wrong = "incidentially" and right = "incidentally" - or - wrong = "incidently" and right = "incidentally" - or - wrong = "inclreased" and right = "increased" - or - wrong = "includ" and right = "include" - or - wrong = "includng" and right = "including" - or - wrong = "incompatabilities" and right = "incompatibilities" - or - wrong = "incompatability" and right = "incompatibility" - or - wrong = "incompatable" and right = "incompatible" - or - wrong = "incompatablities" and right = "incompatibilities" - or - wrong = "incompatablity" and right = "incompatibility" - or - wrong = "incompatiblities" and right = "incompatibilities" - or - wrong = "incompatiblity" and right = "incompatibility" - or - wrong = "incompetance" and right = "incompetence" - or - wrong = "incompetant" and right = "incompetent" - or - wrong = "incomptable" and right = "incompatible" - or - wrong = "incomptetent" and right = "incompetent" - or - wrong = "inconsistant" and right = "inconsistent" - or - wrong = "incoroporated" and right = "incorporated" - or - wrong = "incorperation" and right = "incorporation" - or - wrong = "incorportaed" and right = "incorporated" - or - wrong = "incorprates" and right = "incorporates" - or - wrong = "incorruptable" and right = "incorruptible" - or - wrong = "incramentally" and right = "incrementally" - or - wrong = "increadible" and right = "incredible" - or - wrong = "incredable" and right = "incredible" - or - wrong = "inctroduce" and right = "introduce" - or - wrong = "inctroduced" and right = "introduced" - or - wrong = "incuding" and right = "including" - or - wrong = "incunabla" and right = "incunabula" - or - wrong = "indefinately" and right = "indefinitely" - or - wrong = "indefineable" and right = "undefinable" - or - wrong = "indefinitly" and right = "indefinitely" - or - wrong = "indentical" and right = "identical" - or - wrong = "indepedantly" and right = "independently" - or - wrong = "indepedence" and right = "independence" - or - wrong = "independance" and right = "independence" - or - wrong = "independant" and right = "independent" - or - wrong = "independantly" and right = "independently" - or - wrong = "independece" and right = "independence" - or - wrong = "independendet" and right = "independent" - or - wrong = "indespensable" and right = "indispensable" - or - wrong = "indespensible" and right = "indispensable" - or - wrong = "indictement" and right = "indictment" - or - wrong = "indigineous" and right = "indigenous" - or - wrong = "indipendence" and right = "independence" - or - wrong = "indipendent" and right = "independent" - or - wrong = "indipendently" and right = "independently" - or - wrong = "indispensible" and right = "indispensable" - or - wrong = "indisputible" and right = "indisputable" - or - wrong = "indisputibly" and right = "indisputably" - or - wrong = "indite" and right = "indict" - or - wrong = "individualy" and right = "individually" - or - wrong = "indpendent" and right = "independent" - or - wrong = "indpendently" and right = "independently" - or - wrong = "indulgue" and right = "indulge" - or - wrong = "indutrial" and right = "industrial" - or - wrong = "indviduals" and right = "individuals" - or - wrong = "inefficienty" and right = "inefficiently" - or - wrong = "inevatible" and right = "inevitable" - or - wrong = "inevitible" and right = "inevitable" - or - wrong = "inevititably" and right = "inevitably" - or - wrong = "infalability" and right = "infallibility" - or - wrong = "infallable" and right = "infallible" - or - wrong = "infectuous" and right = "infectious" - or - wrong = "infered" and right = "inferred" - or - wrong = "infilitrate" and right = "infiltrate" - or - wrong = "infilitrated" and right = "infiltrated" - or - wrong = "infilitration" and right = "infiltration" - or - wrong = "infinit" and right = "infinite" - or - wrong = "inflamation" and right = "inflammation" - or - wrong = "influencial" and right = "influential" - or - wrong = "influented" and right = "influenced" - or - wrong = "infomation" and right = "information" - or - wrong = "informtion" and right = "information" - or - wrong = "infrantryman" and right = "infantryman" - or - wrong = "infrigement" and right = "infringement" - or - wrong = "ingenius" and right = "ingenious" - or - wrong = "ingreediants" and right = "ingredients" - or - wrong = "inhabitans" and right = "inhabitants" - or - wrong = "inherantly" and right = "inherently" - or - wrong = "inheritage" and right = "heritage" - or - wrong = "inheritage" and right = "inheritance" - or - wrong = "inheritence" and right = "inheritance" - or - wrong = "inital" and right = "initial" - or - wrong = "initalize" and right = "initialize" - or - wrong = "initally" and right = "initially" - or - wrong = "initation" and right = "initiation" - or - wrong = "initiaitive" and right = "initiative" - or - wrong = "inlcuding" and right = "including" - or - wrong = "inmigrant" and right = "immigrant" - or - wrong = "inmigrants" and right = "immigrants" - or - wrong = "innoculated" and right = "inoculated" - or - wrong = "inocence" and right = "innocence" - or - wrong = "inofficial" and right = "unofficial" - or - wrong = "inot" and right = "into" - or - wrong = "inpeach" and right = "impeach" - or - wrong = "inpending" and right = "impending" - or - wrong = "inpenetrable" and right = "impenetrable" - or - wrong = "inpolite" and right = "impolite" - or - wrong = "inprisonment" and right = "imprisonment" - or - wrong = "inproving" and right = "improving" - or - wrong = "insectiverous" and right = "insectivorous" - or - wrong = "insensative" and right = "insensitive" - or - wrong = "inseperable" and right = "inseparable" - or - wrong = "insistance" and right = "insistence" - or - wrong = "insitution" and right = "institution" - or - wrong = "insitutions" and right = "institutions" - or - wrong = "inspite" and right = "inspire" - or - wrong = "instade" and right = "instead" - or - wrong = "instatance" and right = "instance" - or - wrong = "institue" and right = "institute" - or - wrong = "instuction" and right = "instruction" - or - wrong = "instuments" and right = "instruments" - or - wrong = "instutionalized" and right = "institutionalized" - or - wrong = "instutions" and right = "intuitions" - or - wrong = "insurence" and right = "insurance" - or - wrong = "intelectual" and right = "intellectual" - or - wrong = "inteligence" and right = "intelligence" - or - wrong = "inteligent" and right = "intelligent" - or - wrong = "intenational" and right = "international" - or - wrong = "intented" and right = "indented" - or - wrong = "intented" and right = "intended" - or - wrong = "intepretation" and right = "interpretation" - or - wrong = "intepretator" and right = "interpretor" - or - wrong = "interational" and right = "international" - or - wrong = "interbread" and right = "interbred" - or - wrong = "interbread" and right = "interbreed" - or - wrong = "interchangable" and right = "interchangeable" - or - wrong = "interchangably" and right = "interchangeably" - or - wrong = "intercontinential" and right = "intercontinental" - or - wrong = "intercontinetal" and right = "intercontinental" - or - wrong = "intered" and right = "interned" - or - wrong = "intered" and right = "interred" - or - wrong = "interelated" and right = "interrelated" - or - wrong = "interferance" and right = "interference" - or - wrong = "interfereing" and right = "interfering" - or - wrong = "intergrated" and right = "integrated" - or - wrong = "intergration" and right = "integration" - or - wrong = "interm" and right = "interim" - or - wrong = "internation" and right = "international" - or - wrong = "interpet" and right = "interpret" - or - wrong = "interrim" and right = "interim" - or - wrong = "interrugum" and right = "interregnum" - or - wrong = "intertaining" and right = "entertaining" - or - wrong = "interupt" and right = "interrupt" - or - wrong = "intervines" and right = "intervenes" - or - wrong = "intevene" and right = "intervene" - or - wrong = "intial" and right = "initial" - or - wrong = "intialize" and right = "initialize" - or - wrong = "intialized" and right = "initialized" - or - wrong = "intially" and right = "initially" - or - wrong = "intrduced" and right = "introduced" - or - wrong = "intrest" and right = "interest" - or - wrong = "introdued" and right = "introduced" - or - wrong = "intruduced" and right = "introduced" - or - wrong = "intrument" and right = "instrument" - or - wrong = "intrumental" and right = "instrumental" - or - wrong = "intruments" and right = "instruments" - or - wrong = "intrusted" and right = "entrusted" - or - wrong = "intutive" and right = "intuitive" - or - wrong = "intutively" and right = "intuitively" - or - wrong = "inudstry" and right = "industry" - or - wrong = "inumerable" and right = "enumerable" - or - wrong = "inumerable" and right = "innumerable" - or - wrong = "inventer" and right = "inventor" - or - wrong = "invertibrates" and right = "invertebrates" - or - wrong = "investingate" and right = "investigate" - or - wrong = "involvment" and right = "involvement" - or - wrong = "irelevent" and right = "irrelevant" - or - wrong = "iresistable" and right = "irresistible" - or - wrong = "iresistably" and right = "irresistibly" - or - wrong = "iresistible" and right = "irresistible" - or - wrong = "iresistibly" and right = "irresistibly" - or - wrong = "iritable" and right = "irritable" - or - wrong = "iritated" and right = "irritated" - or - wrong = "ironicly" and right = "ironically" - or - wrong = "irregardless" and right = "regardless" - or - wrong = "irrelevent" and right = "irrelevant" - or - wrong = "irreplacable" and right = "irreplaceable" - or - wrong = "irresistable" and right = "irresistible" - or - wrong = "irresistably" and right = "irresistibly" - or - wrong = "israelies" and right = "israelis" - or - wrong = "issueing" and right = "issuing" - or - wrong = "itnroduced" and right = "introduced" - or - wrong = "iunior" and right = "junior" - or - wrong = "iwll" and right = "will" - or - wrong = "iwth" and right = "with" - or - wrong = "janurary" and right = "january" - or - wrong = "januray" and right = "january" - or - wrong = "japanes" and right = "japanese" - or - wrong = "jaques" and right = "jacques" - or - wrong = "jeapardy" and right = "jeopardy" - or - wrong = "jewllery" and right = "jewellery" - or - wrong = "johanine" and right = "johannine" - or - wrong = "jorunal" and right = "journal" - or - wrong = "jospeh" and right = "joseph" - or - wrong = "jouney" and right = "journey" - or - wrong = "journied" and right = "journeyed" - or - wrong = "journies" and right = "journeys" - or - wrong = "jstu" and right = "just" - or - wrong = "jsut" and right = "just" - or - wrong = "juadaism" and right = "judaism" - or - wrong = "juadism" and right = "judaism" - or - wrong = "judical" and right = "judicial" - or - wrong = "judisuary" and right = "judiciary" - or - wrong = "juducial" and right = "judicial" - or - wrong = "juristiction" and right = "jurisdiction" - or - wrong = "juristictions" and right = "jurisdictions" - or - wrong = "kindergarden" and right = "kindergarten" - or - wrong = "klenex" and right = "kleenex" - or - wrong = "knifes" and right = "knives" - or - wrong = "knive" and right = "knife" - or - wrong = "knowlege" and right = "knowledge" - or - wrong = "knowlegeable" and right = "knowledgeable" - or - wrong = "knwo" and right = "know" - or - wrong = "knwos" and right = "knows" - or - wrong = "konw" and right = "know" - or - wrong = "konws" and right = "knows" - or - wrong = "kwno" and right = "know" - or - wrong = "labatory" and right = "laboratory" - or - wrong = "labatory" and right = "lavatory" - or - wrong = "labled" and right = "labeled" - or - wrong = "labled" and right = "labelled" - or - wrong = "labratory" and right = "laboratory" - or - wrong = "laguage" and right = "language" - or - wrong = "laguages" and right = "languages" - or - wrong = "langage" and right = "language" - or - wrong = "langauge" and right = "language" - or - wrong = "larg" and right = "large" - or - wrong = "largst" and right = "largest" - or - wrong = "larrry" and right = "larry" - or - wrong = "lastr" and right = "last" - or - wrong = "lattitude" and right = "latitude" - or - wrong = "launchs" and right = "launch" - or - wrong = "launchs" and right = "launches" - or - wrong = "launhed" and right = "launched" - or - wrong = "lavae" and right = "larvae" - or - wrong = "layed" and right = "laid" - or - wrong = "lazyness" and right = "laziness" - or - wrong = "leage" and right = "league" - or - wrong = "leanr" and right = "lean" - or - wrong = "leanr" and right = "leaner" - or - wrong = "leanr" and right = "learn" - or - wrong = "leathal" and right = "lethal" - or - wrong = "lefted" and right = "left" - or - wrong = "legitamate" and right = "legitimate" - or - wrong = "legitmate" and right = "legitimate" - or - wrong = "leibnitz" and right = "leibniz" - or - wrong = "lengh" and right = "length" - or - wrong = "lenght" and right = "length" - or - wrong = "lengt" and right = "length" - or - wrong = "lenth" and right = "length" - or - wrong = "leran" and right = "learn" - or - wrong = "lerans" and right = "learns" - or - wrong = "leutenant" and right = "lieutenant" - or - wrong = "levetate" and right = "levitate" - or - wrong = "levetated" and right = "levitated" - or - wrong = "levetates" and right = "levitates" - or - wrong = "levetating" and right = "levitating" - or - wrong = "levle" and right = "level" - or - wrong = "liasion" and right = "liaison" - or - wrong = "liason" and right = "liaison" - or - wrong = "liasons" and right = "liaisons" - or - wrong = "libaries" and right = "libraries" - or - wrong = "libary" and right = "library" - or - wrong = "libell" and right = "libel" - or - wrong = "libguistic" and right = "linguistic" - or - wrong = "libguistics" and right = "linguistics" - or - wrong = "libitarianisn" and right = "libertarianism" - or - wrong = "lible" and right = "liable" - or - wrong = "lible" and right = "libel" - or - wrong = "lieing" and right = "lying" - or - wrong = "liek" and right = "like" - or - wrong = "liekd" and right = "liked" - or - wrong = "liesure" and right = "leisure" - or - wrong = "lieuenant" and right = "lieutenant" - or - wrong = "lieved" and right = "lived" - or - wrong = "liftime" and right = "lifetime" - or - wrong = "likelyhood" and right = "likelihood" - or - wrong = "linnaena" and right = "linnaean" - or - wrong = "lippizaner" and right = "lipizzaner" - or - wrong = "liquify" and right = "liquefy" - or - wrong = "liscense" and right = "licence" - or - wrong = "liscense" and right = "license" - or - wrong = "lisence" and right = "licence" - or - wrong = "lisence" and right = "license" - or - wrong = "lisense" and right = "licence" - or - wrong = "lisense" and right = "license" - or - wrong = "listners" and right = "listeners" - or - wrong = "litature" and right = "literature" - or - wrong = "literaly" and right = "literally" - or - wrong = "literture" and right = "literature" - or - wrong = "littel" and right = "little" - or - wrong = "litterally" and right = "literally" - or - wrong = "liuke" and right = "like" - or - wrong = "livley" and right = "lively" - or - wrong = "lmits" and right = "limits" - or - wrong = "loev" and right = "love" - or - wrong = "lonelyness" and right = "loneliness" - or - wrong = "longitudonal" and right = "longitudinal" - or - wrong = "lonley" and right = "lonely" - or - wrong = "lonly" and right = "lonely" - or - wrong = "lonly" and right = "only" - or - wrong = "loosing" and right = "losing" - or - wrong = "lotharingen" and right = "lothringen" - or - wrong = "lsat" and right = "last" - or - wrong = "lukid" and right = "likud" - or - wrong = "lveo" and right = "love" - or - wrong = "lvoe" and right = "love" - or - wrong = "lybia" and right = "libya" - or - wrong = "maching" and right = "machine" - or - wrong = "maching" and right = "marching" - or - wrong = "maching" and right = "matching" - or - wrong = "mackeral" and right = "mackerel" - or - wrong = "magasine" and right = "magazine" - or - wrong = "magincian" and right = "magician" - or - wrong = "magisine" and right = "magazine" - or - wrong = "magizine" and right = "magazine" - or - wrong = "magnificient" and right = "magnificent" - or - wrong = "magolia" and right = "magnolia" - or - wrong = "mailny" and right = "mainly" - or - wrong = "maintainance" and right = "maintenance" - or - wrong = "maintainence" and right = "maintenance" - or - wrong = "maintance" and right = "maintenance" - or - wrong = "maintenence" and right = "maintenance" - or - wrong = "maintinaing" and right = "maintaining" - or - wrong = "maintioned" and right = "mentioned" - or - wrong = "majoroty" and right = "majority" - or - wrong = "maked" and right = "made" - or - wrong = "maked" and right = "marked" - or - wrong = "makse" and right = "makes" - or - wrong = "malcom" and right = "malcolm" - or - wrong = "maltesian" and right = "maltese" - or - wrong = "mamal" and right = "mammal" - or - wrong = "mamalian" and right = "mammalian" - or - wrong = "managable" and right = "manageable" - or - wrong = "managable" and right = "manageably" - or - wrong = "managment" and right = "management" - or - wrong = "maneouvre" and right = "manoeuvre" - or - wrong = "maneouvred" and right = "manoeuvred" - or - wrong = "maneouvres" and right = "manoeuvres" - or - wrong = "maneouvring" and right = "manoeuvring" - or - wrong = "manisfestations" and right = "manifestations" - or - wrong = "manoeuverability" and right = "maneuverability" - or - wrong = "manouver" and right = "maneuver" - or - wrong = "manouver" and right = "manoeuvre" - or - wrong = "manouverability" and right = "maneuverability" - or - wrong = "manouverability" and right = "manoeuverability" - or - wrong = "manouverability" and right = "manoeuvrability" - or - wrong = "manouverable" and right = "maneuverable" - or - wrong = "manouverable" and right = "manoeuvrable" - or - wrong = "manouvers" and right = "maneuvers" - or - wrong = "manouvers" and right = "manoeuvres" - or - wrong = "mantained" and right = "maintained" - or - wrong = "manuever" and right = "maneuver" - or - wrong = "manuever" and right = "manoeuvre" - or - wrong = "manuevers" and right = "maneuvers" - or - wrong = "manuevers" and right = "manoeuvres" - or - wrong = "manufacturedd" and right = "manufactured" - or - wrong = "manufature" and right = "manufacture" - or - wrong = "manufatured" and right = "manufactured" - or - wrong = "manufaturing" and right = "manufacturing" - or - wrong = "manuver" and right = "maneuver" - or - wrong = "mariage" and right = "marriage" - or - wrong = "marjority" and right = "majority" - or - wrong = "markes" and right = "marks" - or - wrong = "marketting" and right = "marketing" - or - wrong = "marmelade" and right = "marmalade" - or - wrong = "marrage" and right = "marriage" - or - wrong = "marraige" and right = "marriage" - or - wrong = "marrtyred" and right = "martyred" - or - wrong = "marryied" and right = "married" - or - wrong = "massachussets" and right = "massachusetts" - or - wrong = "massachussetts" and right = "massachusetts" - or - wrong = "masterbation" and right = "masturbation" - or - wrong = "mataphysical" and right = "metaphysical" - or - wrong = "materalists" and right = "materialist" - or - wrong = "mathamatics" and right = "mathematics" - or - wrong = "mathematican" and right = "mathematician" - or - wrong = "mathematicas" and right = "mathematics" - or - wrong = "matheticians" and right = "mathematicians" - or - wrong = "mathmatically" and right = "mathematically" - or - wrong = "mathmatician" and right = "mathematician" - or - wrong = "mathmaticians" and right = "mathematicians" - or - wrong = "mccarthyst" and right = "mccarthyist" - or - wrong = "mchanics" and right = "mechanics" - or - wrong = "meaninng" and right = "meaning" - or - wrong = "mear" and right = "mare" - or - wrong = "mear" and right = "mere" - or - wrong = "mear" and right = "wear" - or - wrong = "mechandise" and right = "merchandise" - or - wrong = "medacine" and right = "medicine" - or - wrong = "medeival" and right = "medieval" - or - wrong = "medevial" and right = "medieval" - or - wrong = "mediciney" and right = "mediciny" - or - wrong = "medievel" and right = "medieval" - or - wrong = "mediterainnean" and right = "mediterranean" - or - wrong = "mediteranean" and right = "mediterranean" - or - wrong = "meerkrat" and right = "meerkat" - or - wrong = "melieux" and right = "milieux" - or - wrong = "membranaphone" and right = "membranophone" - or - wrong = "memeber" and right = "member" - or - wrong = "menally" and right = "mentally" - or - wrong = "meranda" and right = "miranda" - or - wrong = "meranda" and right = "veranda" - or - wrong = "mercentile" and right = "mercantile" - or - wrong = "mesage" and right = "message" - or - wrong = "messanger" and right = "messenger" - or - wrong = "messenging" and right = "messaging" - or - wrong = "messsage" and right = "message" - or - wrong = "metalic" and right = "metallic" - or - wrong = "metalurgic" and right = "metallurgic" - or - wrong = "metalurgical" and right = "metallurgical" - or - wrong = "metalurgy" and right = "metallurgy" - or - wrong = "metamorphysis" and right = "metamorphosis" - or - wrong = "metaphoricial" and right = "metaphorical" - or - wrong = "meterologist" and right = "meteorologist" - or - wrong = "meterology" and right = "meteorology" - or - wrong = "methaphor" and right = "metaphor" - or - wrong = "methaphors" and right = "metaphors" - or - wrong = "michagan" and right = "michigan" - or - wrong = "micoscopy" and right = "microscopy" - or - wrong = "midwifes" and right = "midwives" - or - wrong = "mileau" and right = "milieu" - or - wrong = "milennia" and right = "millennia" - or - wrong = "milennium" and right = "millennium" - or - wrong = "mileu" and right = "milieu" - or - wrong = "miliary" and right = "military" - or - wrong = "miligram" and right = "milligram" - or - wrong = "milion" and right = "million" - or - wrong = "miliraty" and right = "military" - or - wrong = "millenia" and right = "millennia" - or - wrong = "millenial" and right = "millennial" - or - wrong = "millenialism" and right = "millennialism" - or - wrong = "millenium" and right = "millennium" - or - wrong = "millepede" and right = "millipede" - or - wrong = "millioniare" and right = "millionaire" - or - wrong = "millitant" and right = "militant" - or - wrong = "millitary" and right = "military" - or - wrong = "millon" and right = "million" - or - wrong = "miltary" and right = "military" - or - wrong = "minature" and right = "miniature" - or - wrong = "minerial" and right = "mineral" - or - wrong = "ministery" and right = "ministry" - or - wrong = "minsitry" and right = "ministry" - or - wrong = "minstries" and right = "ministries" - or - wrong = "minstry" and right = "ministry" - or - wrong = "minumum" and right = "minimum" - or - wrong = "mirrorred" and right = "mirrored" - or - wrong = "miscelaneous" and right = "miscellaneous" - or - wrong = "miscellanious" and right = "miscellaneous" - or - wrong = "miscellanous" and right = "miscellaneous" - or - wrong = "mischeivous" and right = "mischievous" - or - wrong = "mischevious" and right = "mischievous" - or - wrong = "mischievious" and right = "mischievous" - or - wrong = "misdameanor" and right = "misdemeanor" - or - wrong = "misdameanors" and right = "misdemeanors" - or - wrong = "misdemenor" and right = "misdemeanor" - or - wrong = "misdemenors" and right = "misdemeanors" - or - wrong = "misfourtunes" and right = "misfortunes" - or - wrong = "misile" and right = "missile" - or - wrong = "misouri" and right = "missouri" - or - wrong = "mispell" and right = "misspell" - or - wrong = "mispelled" and right = "misspelled" - or - wrong = "mispelling" and right = "misspelling" - or - wrong = "missen" and right = "mizzen" - or - wrong = "missisipi" and right = "mississippi" - or - wrong = "missisippi" and right = "mississippi" - or - wrong = "missle" and right = "missile" - or - wrong = "missonary" and right = "missionary" - or - wrong = "misterious" and right = "mysterious" - or - wrong = "mistery" and right = "mystery" - or - wrong = "misteryous" and right = "mysterious" - or - wrong = "mkae" and right = "make" - or - wrong = "mkaes" and right = "makes" - or - wrong = "mkaing" and right = "making" - or - wrong = "mkea" and right = "make" - or - wrong = "moderm" and right = "modem" - or - wrong = "modle" and right = "model" - or - wrong = "moent" and right = "moment" - or - wrong = "moeny" and right = "money" - or - wrong = "mohammedans" and right = "muslims" - or - wrong = "moil" and right = "mohel" - or - wrong = "moil" and right = "soil" - or - wrong = "moleclues" and right = "molecules" - or - wrong = "momento" and right = "memento" - or - wrong = "monestaries" and right = "monasteries" - or - wrong = "monestary" and right = "monastery" - or - wrong = "monestary" and right = "monetary" - or - wrong = "monickers" and right = "monikers" - or - wrong = "monolite" and right = "monolithic" - or - wrong = "monserrat" and right = "montserrat" - or - wrong = "montains" and right = "mountains" - or - wrong = "montanous" and right = "mountainous" - or - wrong = "montnana" and right = "montana" - or - wrong = "monts" and right = "months" - or - wrong = "montypic" and right = "monotypic" - or - wrong = "moreso" and right = "more" - or - wrong = "morgage" and right = "mortgage" - or - wrong = "morisette" and right = "morissette" - or - wrong = "morrisette" and right = "morissette" - or - wrong = "morroccan" and right = "moroccan" - or - wrong = "morrocco" and right = "morocco" - or - wrong = "morroco" and right = "morocco" - or - wrong = "mortage" and right = "mortgage" - or - wrong = "mosture" and right = "moisture" - or - wrong = "motiviated" and right = "motivated" - or - wrong = "mounth" and right = "month" - or - wrong = "movei" and right = "movie" - or - wrong = "movment" and right = "movement" - or - wrong = "mroe" and right = "more" - or - wrong = "mucuous" and right = "mucous" - or - wrong = "muder" and right = "murder" - or - wrong = "mudering" and right = "murdering" - or - wrong = "muhammadan" and right = "muslim" - or - wrong = "multicultralism" and right = "multiculturalism" - or - wrong = "multipled" and right = "multiplied" - or - wrong = "multiplers" and right = "multipliers" - or - wrong = "munbers" and right = "numbers" - or - wrong = "muncipalities" and right = "municipalities" - or - wrong = "muncipality" and right = "municipality" - or - wrong = "munnicipality" and right = "municipality" - or - wrong = "muscels" and right = "muscles" - or - wrong = "muscels" and right = "mussels" - or - wrong = "muscial" and right = "musical" - or - wrong = "muscician" and right = "musician" - or - wrong = "muscicians" and right = "musicians" - or - wrong = "mutiliated" and right = "mutilated" - or - wrong = "mutiple" and right = "multiple" - or - wrong = "myraid" and right = "myriad" - or - wrong = "mysef" and right = "myself" - or - wrong = "mysogynist" and right = "misogynist" - or - wrong = "mysogyny" and right = "misogyny" - or - wrong = "mysterous" and right = "mysterious" - or - wrong = "mythraic" and right = "mithraic" - or - wrong = "naieve" and right = "naive" - or - wrong = "naploeon" and right = "napoleon" - or - wrong = "napolean" and right = "napoleon" - or - wrong = "napoleonian" and right = "napoleonic" - or - wrong = "naturaly" and right = "naturally" - or - wrong = "naturely" and right = "naturally" - or - wrong = "naturual" and right = "natural" - or - wrong = "naturually" and right = "naturally" - or - wrong = "nazereth" and right = "nazareth" - or - wrong = "neccesarily" and right = "necessarily" - or - wrong = "neccesary" and right = "necessary" - or - wrong = "neccessarily" and right = "necessarily" - or - wrong = "neccessary" and right = "necessary" - or - wrong = "neccessities" and right = "necessities" - or - wrong = "necesarily" and right = "necessarily" - or - wrong = "necesary" and right = "necessary" - or - wrong = "necessiate" and right = "necessitate" - or - wrong = "neglible" and right = "negligible" - or - wrong = "negligable" and right = "negligible" - or - wrong = "negociate" and right = "negotiate" - or - wrong = "negociation" and right = "negotiation" - or - wrong = "negociations" and right = "negotiations" - or - wrong = "negotation" and right = "negotiation" - or - wrong = "neice" and right = "nice" - or - wrong = "neice" and right = "niece" - or - wrong = "neigborhood" and right = "neighborhood" - or - wrong = "neigbour" and right = "neighbor" - or - wrong = "neigbour" and right = "neighbour" - or - wrong = "neigbourhood" and right = "neighbourhood" - or - wrong = "neigbouring" and right = "neighboring" - or - wrong = "neigbouring" and right = "neighbouring" - or - wrong = "neigbours" and right = "neighbors" - or - wrong = "neigbours" and right = "neighbours" - or - wrong = "neolitic" and right = "neolithic" - or - wrong = "nessasarily" and right = "necessarily" - or - wrong = "nessecary" and right = "necessary" - or - wrong = "nestin" and right = "nesting" - or - wrong = "neverthless" and right = "nevertheless" - or - wrong = "newletters" and right = "newsletters" - or - wrong = "nickle" and right = "nickel" - or - wrong = "nightime" and right = "nighttime" - or - wrong = "nineth" and right = "ninth" - or - wrong = "ninteenth" and right = "nineteenth" - or - wrong = "ninty" and right = "ninety" - or - wrong = "nkow" and right = "know" - or - wrong = "nkwo" and right = "know" - or - wrong = "nmae" and right = "name" - or - wrong = "noncombatents" and right = "noncombatants" - or - wrong = "nonsence" and right = "nonsense" - or - wrong = "nontheless" and right = "nonetheless" - or - wrong = "norhern" and right = "northern" - or - wrong = "northen" and right = "northern" - or - wrong = "northereastern" and right = "northeastern" - or - wrong = "notabley" and right = "notably" - or - wrong = "noteable" and right = "notable" - or - wrong = "noteably" and right = "notably" - or - wrong = "noteriety" and right = "notoriety" - or - wrong = "noth" and right = "north" - or - wrong = "nothern" and right = "northern" - or - wrong = "noticable" and right = "noticeable" - or - wrong = "noticably" and right = "noticeably" - or - wrong = "notications" and right = "notifications" - or - wrong = "noticeing" and right = "noticing" - or - wrong = "noticible" and right = "noticeable" - or - wrong = "notwhithstanding" and right = "notwithstanding" - or - wrong = "noveau" and right = "nouveau" - or - wrong = "novermber" and right = "november" - or - wrong = "nowdays" and right = "nowadays" - or - wrong = "nowe" and right = "now" - or - wrong = "nto" and right = "not" - or - wrong = "nubmer" and right = "number" - or - wrong = "nucular" and right = "nuclear" - or - wrong = "nuculear" and right = "nuclear" - or - wrong = "nuisanse" and right = "nuisance" - or - wrong = "nullabour" and right = "nullarbor" - or - wrong = "numberous" and right = "numerous" - or - wrong = "nuremburg" and right = "nuremberg" - or - wrong = "nusance" and right = "nuisance" - or - wrong = "nutritent" and right = "nutrient" - or - wrong = "nutritents" and right = "nutrients" - or - wrong = "nuturing" and right = "nurturing" - or - wrong = "obect" and right = "object" - or - wrong = "obediance" and right = "obedience" - or - wrong = "obediant" and right = "obedient" - or - wrong = "obejct" and right = "object" - or - wrong = "obession" and right = "obsession" - or - wrong = "obssessed" and right = "obsessed" - or - wrong = "obstacal" and right = "obstacle" - or - wrong = "obstancles" and right = "obstacles" - or - wrong = "obstruced" and right = "obstructed" - or - wrong = "ocasion" and right = "occasion" - or - wrong = "ocasional" and right = "occasional" - or - wrong = "ocasionally" and right = "occasionally" - or - wrong = "ocasionaly" and right = "occasionally" - or - wrong = "ocasioned" and right = "occasioned" - or - wrong = "ocasions" and right = "occasions" - or - wrong = "ocassion" and right = "occasion" - or - wrong = "ocassional" and right = "occasional" - or - wrong = "ocassionally" and right = "occasionally" - or - wrong = "ocassionaly" and right = "occasionally" - or - wrong = "ocassioned" and right = "occasioned" - or - wrong = "ocassions" and right = "occasions" - or - wrong = "occaison" and right = "occasion" - or - wrong = "occassion" and right = "occasion" - or - wrong = "occassional" and right = "occasional" - or - wrong = "occassionally" and right = "occasionally" - or - wrong = "occassionaly" and right = "occasionally" - or - wrong = "occassioned" and right = "occasioned" - or - wrong = "occassions" and right = "occasions" - or - wrong = "occationally" and right = "occasionally" - or - wrong = "occour" and right = "occur" - or - wrong = "occurance" and right = "occurrence" - or - wrong = "occurances" and right = "occurrences" - or - wrong = "occured" and right = "occurred" - or - wrong = "occurence" and right = "occurrence" - or - wrong = "occurences" and right = "occurrences" - or - wrong = "occuring" and right = "occurring" - or - wrong = "occurr" and right = "occur" - or - wrong = "occurrance" and right = "occurrence" - or - wrong = "occurrances" and right = "occurrences" - or - wrong = "octohedra" and right = "octahedra" - or - wrong = "octohedral" and right = "octahedral" - or - wrong = "octohedron" and right = "octahedron" - or - wrong = "ocuntries" and right = "countries" - or - wrong = "ocuntry" and right = "country" - or - wrong = "ocurr" and right = "occur" - or - wrong = "ocurrance" and right = "occurrence" - or - wrong = "ocurred" and right = "occurred" - or - wrong = "ocurrence" and right = "occurrence" - or - wrong = "offcers" and right = "officers" - or - wrong = "offcially" and right = "officially" - or - wrong = "offereings" and right = "offerings" - or - wrong = "offical" and right = "official" - or - wrong = "offically" and right = "officially" - or - wrong = "officals" and right = "officials" - or - wrong = "officaly" and right = "officially" - or - wrong = "officialy" and right = "officially" - or - wrong = "offred" and right = "offered" - or - wrong = "oftenly" and right = "often" - or - wrong = "oging" and right = "going" - or - wrong = "oging" and right = "ogling" - or - wrong = "oject" and right = "object" - or - wrong = "omision" and right = "omission" - or - wrong = "omited" and right = "omitted" - or - wrong = "omiting" and right = "omitting" - or - wrong = "omlette" and right = "omelette" - or - wrong = "ommision" and right = "omission" - or - wrong = "ommited" and right = "omitted" - or - wrong = "ommiting" and right = "omitting" - or - wrong = "ommitted" and right = "omitted" - or - wrong = "ommitting" and right = "omitting" - or - wrong = "omniverous" and right = "omnivorous" - or - wrong = "omniverously" and right = "omnivorously" - or - wrong = "omre" and right = "more" - or - wrong = "onot" and right = "not" - or - wrong = "onot" and right = "note" - or - wrong = "onyl" and right = "only" - or - wrong = "openess" and right = "openness" - or - wrong = "oponent" and right = "opponent" - or - wrong = "oportunity" and right = "opportunity" - or - wrong = "opose" and right = "oppose" - or - wrong = "oposite" and right = "opposite" - or - wrong = "oposition" and right = "opposition" - or - wrong = "oppenly" and right = "openly" - or - wrong = "oppinion" and right = "opinion" - or - wrong = "opponant" and right = "opponent" - or - wrong = "oppononent" and right = "opponent" - or - wrong = "oppositition" and right = "opposition" - or - wrong = "oppossed" and right = "opposed" - or - wrong = "opprotunity" and right = "opportunity" - or - wrong = "opression" and right = "oppression" - or - wrong = "opressive" and right = "oppressive" - or - wrong = "opthalmic" and right = "ophthalmic" - or - wrong = "opthalmologist" and right = "ophthalmologist" - or - wrong = "opthalmology" and right = "ophthalmology" - or - wrong = "opthamologist" and right = "ophthalmologist" - or - wrong = "optmizations" and right = "optimizations" - or - wrong = "optomism" and right = "optimism" - or - wrong = "orded" and right = "ordered" - or - wrong = "organim" and right = "organism" - or - wrong = "organistion" and right = "organisation" - or - wrong = "organiztion" and right = "organization" - or - wrong = "orgin" and right = "organ" - or - wrong = "orgin" and right = "origin" - or - wrong = "orginal" and right = "original" - or - wrong = "orginally" and right = "originally" - or - wrong = "orginize" and right = "organise" - or - wrong = "oridinarily" and right = "ordinarily" - or - wrong = "origanaly" and right = "originally" - or - wrong = "originall" and right = "original" - or - wrong = "originall" and right = "originally" - or - wrong = "originaly" and right = "originally" - or - wrong = "originially" and right = "originally" - or - wrong = "originnally" and right = "originally" - or - wrong = "origional" and right = "original" - or - wrong = "orignally" and right = "originally" - or - wrong = "orignially" and right = "originally" - or - wrong = "otehr" and right = "other" - or - wrong = "oublisher" and right = "publisher" - or - wrong = "ouevre" and right = "oeuvre" - or - wrong = "ouput" and right = "output" - or - wrong = "oustanding" and right = "outstanding" - or - wrong = "overriden" and right = "overridden" - or - wrong = "overshaddowed" and right = "overshadowed" - or - wrong = "overwelming" and right = "overwhelming" - or - wrong = "overwheliming" and right = "overwhelming" - or - wrong = "owrk" and right = "work" - or - wrong = "owudl" and right = "would" - or - wrong = "oxigen" and right = "oxygen" - or - wrong = "oximoron" and right = "oxymoron" - or - wrong = "p0enis" and right = "penis" - or - wrong = "paide" and right = "paid" - or - wrong = "paitience" and right = "patience" - or - wrong = "palce" and right = "palace" - or - wrong = "palce" and right = "place" - or - wrong = "paleolitic" and right = "paleolithic" - or - wrong = "paliamentarian" and right = "parliamentarian" - or - wrong = "palistian" and right = "palestinian" - or - wrong = "palistinian" and right = "palestinian" - or - wrong = "palistinians" and right = "palestinians" - or - wrong = "pallete" and right = "palette" - or - wrong = "pamflet" and right = "pamphlet" - or - wrong = "pamplet" and right = "pamphlet" - or - wrong = "pantomine" and right = "pantomime" - or - wrong = "papanicalou" and right = "papanicolaou" - or - wrong = "paralel" and right = "parallel" - or - wrong = "paralell" and right = "parallel" - or - wrong = "paralelly" and right = "parallelly" - or - wrong = "paralely" and right = "parallelly" - or - wrong = "parallely" and right = "parallelly" - or - wrong = "paramater" and right = "parameter" - or - wrong = "paramters" and right = "parameters" - or - wrong = "parametarized" and right = "parameterized" - or - wrong = "paranthesis" and right = "parenthesis" - or - wrong = "paraphenalia" and right = "paraphernalia" - or - wrong = "parellels" and right = "parallels" - or - wrong = "parisitic" and right = "parasitic" - or - wrong = "parituclar" and right = "particular" - or - wrong = "parliment" and right = "parliament" - or - wrong = "parrakeets" and right = "parakeets" - or - wrong = "parralel" and right = "parallel" - or - wrong = "parrallel" and right = "parallel" - or - wrong = "parrallell" and right = "parallel" - or - wrong = "parrallelly" and right = "parallelly" - or - wrong = "parrallely" and right = "parallelly" - or - wrong = "partialy" and right = "partially" - or - wrong = "particually" and right = "particularly" - or - wrong = "particualr" and right = "particular" - or - wrong = "particuarly" and right = "particularly" - or - wrong = "particularily" and right = "particularly" - or - wrong = "particulary" and right = "particularly" - or - wrong = "pary" and right = "party" - or - wrong = "pased" and right = "passed" - or - wrong = "pasengers" and right = "passengers" - or - wrong = "passerbys" and right = "passersby" - or - wrong = "pasttime" and right = "pastime" - or - wrong = "pastural" and right = "pastoral" - or - wrong = "paticular" and right = "particular" - or - wrong = "pattented" and right = "patented" - or - wrong = "pavillion" and right = "pavilion" - or - wrong = "payed" and right = "paid" - or - wrong = "pblisher" and right = "publisher" - or - wrong = "pbulisher" and right = "publisher" - or - wrong = "peageant" and right = "pageant" - or - wrong = "peaple" and right = "people" - or - wrong = "peaples" and right = "peoples" - or - wrong = "peculure" and right = "peculiar" - or - wrong = "pedestrain" and right = "pedestrian" - or - wrong = "peformed" and right = "performed" - or - wrong = "peice" and right = "piece" - or - wrong = "peloponnes" and right = "peloponnesus" - or - wrong = "penatly" and right = "penalty" - or - wrong = "penerator" and right = "penetrator" - or - wrong = "penisula" and right = "peninsula" - or - wrong = "penisular" and right = "peninsular" - or - wrong = "penninsula" and right = "peninsula" - or - wrong = "penninsular" and right = "peninsular" - or - wrong = "pennisula" and right = "peninsula" - or - wrong = "pennyslvania" and right = "pennsylvania" - or - wrong = "pensinula" and right = "peninsula" - or - wrong = "pensle" and right = "pencil" - or - wrong = "peom" and right = "poem" - or - wrong = "peoms" and right = "poems" - or - wrong = "peopel" and right = "people" - or - wrong = "peopels" and right = "peoples" - or - wrong = "peotry" and right = "poetry" - or - wrong = "perade" and right = "parade" - or - wrong = "percepted" and right = "perceived" - or - wrong = "percieve" and right = "perceive" - or - wrong = "percieved" and right = "perceived" - or - wrong = "perenially" and right = "perennially" - or - wrong = "peretrator" and right = "perpetrator" - or - wrong = "perfomance" and right = "performance" - or - wrong = "perfomers" and right = "performers" - or - wrong = "performence" and right = "performance" - or - wrong = "performes" and right = "performed" - or - wrong = "performes" and right = "performs" - or - wrong = "perhasp" and right = "perhaps" - or - wrong = "perheaps" and right = "perhaps" - or - wrong = "perhpas" and right = "perhaps" - or - wrong = "peripathetic" and right = "peripatetic" - or - wrong = "peristent" and right = "persistent" - or - wrong = "perjery" and right = "perjury" - or - wrong = "perjorative" and right = "pejorative" - or - wrong = "permanant" and right = "permanent" - or - wrong = "permenant" and right = "permanent" - or - wrong = "permenantly" and right = "permanently" - or - wrong = "permissable" and right = "permissible" - or - wrong = "perogative" and right = "prerogative" - or - wrong = "peronal" and right = "personal" - or - wrong = "perosnality" and right = "personality" - or - wrong = "perpertrated" and right = "perpetrated" - or - wrong = "perphas" and right = "perhaps" - or - wrong = "perpindicular" and right = "perpendicular" - or - wrong = "persan" and right = "person" - or - wrong = "perseverence" and right = "perseverance" - or - wrong = "persistance" and right = "persistence" - or - wrong = "persistant" and right = "persistent" - or - wrong = "personel" and right = "personal" - or - wrong = "personel" and right = "personnel" - or - wrong = "personell" and right = "personnel" - or - wrong = "personnell" and right = "personnel" - or - wrong = "persuded" and right = "persuaded" - or - wrong = "persue" and right = "pursue" - or - wrong = "persued" and right = "pursued" - or - wrong = "persuing" and right = "pursuing" - or - wrong = "persuit" and right = "pursuit" - or - wrong = "persuits" and right = "pursuits" - or - wrong = "pertubation" and right = "perturbation" - or - wrong = "pertubations" and right = "perturbations" - or - wrong = "pessiary" and right = "pessary" - or - wrong = "petetion" and right = "petition" - or - wrong = "pharoah" and right = "pharaoh" - or - wrong = "phenomenom" and right = "phenomenon" - or - wrong = "phenomenonal" and right = "phenomenal" - or - wrong = "phenomenonly" and right = "phenomenally" - or - wrong = "phenomonenon" and right = "phenomenon" - or - wrong = "phenomonon" and right = "phenomenon" - or - wrong = "phenonmena" and right = "phenomena" - or - wrong = "philipines" and right = "philippines" - or - wrong = "philisopher" and right = "philosopher" - or - wrong = "philisophical" and right = "philosophical" - or - wrong = "philisophy" and right = "philosophy" - or - wrong = "phillipine" and right = "philippine" - or - wrong = "phillipines" and right = "philippines" - or - wrong = "phillippines" and right = "philippines" - or - wrong = "phillosophically" and right = "philosophically" - or - wrong = "philospher" and right = "philosopher" - or - wrong = "philosphies" and right = "philosophies" - or - wrong = "philosphy" and right = "philosophy" - or - wrong = "phonecian" and right = "phoenecian" - or - wrong = "phongraph" and right = "phonograph" - or - wrong = "phylosophical" and right = "philosophical" - or - wrong = "physicaly" and right = "physically" - or - wrong = "piblisher" and right = "publisher" - or - wrong = "pich" and right = "pitch" - or - wrong = "pilgrimmage" and right = "pilgrimage" - or - wrong = "pilgrimmages" and right = "pilgrimages" - or - wrong = "pinapple" and right = "pineapple" - or - wrong = "pinnaple" and right = "pineapple" - or - wrong = "pinoneered" and right = "pioneered" - or - wrong = "plagarism" and right = "plagiarism" - or - wrong = "planation" and right = "plantation" - or - wrong = "planed" and right = "planned" - or - wrong = "plantiff" and right = "plaintiff" - or - wrong = "plateu" and right = "plateau" - or - wrong = "plausable" and right = "plausible" - or - wrong = "playright" and right = "playwright" - or - wrong = "playwrite" and right = "playwright" - or - wrong = "playwrites" and right = "playwrights" - or - wrong = "pleasent" and right = "pleasant" - or - wrong = "plebicite" and right = "plebiscite" - or - wrong = "plesant" and right = "pleasant" - or - wrong = "poenis" and right = "penis" - or - wrong = "poeoples" and right = "peoples" - or - wrong = "poety" and right = "poetry" - or - wrong = "poisin" and right = "poison" - or - wrong = "polical" and right = "political" - or - wrong = "polinator" and right = "pollinator" - or - wrong = "polinators" and right = "pollinators" - or - wrong = "politican" and right = "politician" - or - wrong = "politicans" and right = "politicians" - or - wrong = "poltical" and right = "political" - or - wrong = "polute" and right = "pollute" - or - wrong = "poluted" and right = "polluted" - or - wrong = "polutes" and right = "pollutes" - or - wrong = "poluting" and right = "polluting" - or - wrong = "polution" and right = "pollution" - or - wrong = "polyphonyic" and right = "polyphonic" - or - wrong = "polysaccaride" and right = "polysaccharide" - or - wrong = "polysaccharid" and right = "polysaccharide" - or - wrong = "pomegranite" and right = "pomegranate" - or - wrong = "pomotion" and right = "promotion" - or - wrong = "poportional" and right = "proportional" - or - wrong = "popoulation" and right = "population" - or - wrong = "popularaty" and right = "popularity" - or - wrong = "populare" and right = "popular" - or - wrong = "populer" and right = "popular" - or - wrong = "porshan" and right = "portion" - or - wrong = "porshon" and right = "portion" - or - wrong = "portait" and right = "portrait" - or - wrong = "portayed" and right = "portrayed" - or - wrong = "portraing" and right = "portraying" - or - wrong = "portugese" and right = "portuguese" - or - wrong = "portuguease" and right = "portuguese" - or - wrong = "portugues" and right = "portuguese" - or - wrong = "posess" and right = "possess" - or - wrong = "posessed" and right = "possessed" - or - wrong = "posesses" and right = "possesses" - or - wrong = "posessing" and right = "possessing" - or - wrong = "posession" and right = "possession" - or - wrong = "posessions" and right = "possessions" - or - wrong = "posion" and right = "poison" - or - wrong = "positon" and right = "position" - or - wrong = "positon" and right = "positron" - or - wrong = "possable" and right = "possible" - or - wrong = "possably" and right = "possibly" - or - wrong = "posseses" and right = "possesses" - or - wrong = "possesing" and right = "possessing" - or - wrong = "possesion" and right = "possession" - or - wrong = "possessess" and right = "possesses" - or - wrong = "possibile" and right = "possible" - or - wrong = "possibilty" and right = "possibility" - or - wrong = "possiblility" and right = "possibility" - or - wrong = "possiblilty" and right = "possibility" - or - wrong = "possiblities" and right = "possibilities" - or - wrong = "possiblity" and right = "possibility" - or - wrong = "possition" and right = "position" - or - wrong = "postdam" and right = "potsdam" - or - wrong = "posthomous" and right = "posthumous" - or - wrong = "postion" and right = "position" - or - wrong = "postive" and right = "positive" - or - wrong = "potatos" and right = "potatoes" - or - wrong = "potrait" and right = "portrait" - or - wrong = "potrayed" and right = "portrayed" - or - wrong = "poulations" and right = "populations" - or - wrong = "poverful" and right = "powerful" - or - wrong = "poweful" and right = "powerful" - or - wrong = "powerfull" and right = "powerful" - or - wrong = "ppublisher" and right = "publisher" - or - wrong = "practial" and right = "practical" - or - wrong = "practially" and right = "practically" - or - wrong = "practicaly" and right = "practically" - or - wrong = "practicioner" and right = "practitioner" - or - wrong = "practicioners" and right = "practitioners" - or - wrong = "practicly" and right = "practically" - or - wrong = "practioner" and right = "practitioner" - or - wrong = "practioners" and right = "practitioners" - or - wrong = "prairy" and right = "prairie" - or - wrong = "prarie" and right = "prairie" - or - wrong = "praries" and right = "prairies" - or - wrong = "pratice" and right = "practice" - or - wrong = "preample" and right = "preamble" - or - wrong = "precedessor" and right = "predecessor" - or - wrong = "preceed" and right = "precede" - or - wrong = "preceeded" and right = "preceded" - or - wrong = "preceeding" and right = "preceding" - or - wrong = "preceeds" and right = "precedes" - or - wrong = "precendence" and right = "precedence" - or - wrong = "precentage" and right = "percentage" - or - wrong = "precice" and right = "precise" - or - wrong = "precisly" and right = "precisely" - or - wrong = "precurser" and right = "precursor" - or - wrong = "predecesors" and right = "predecessors" - or - wrong = "predicatble" and right = "predictable" - or - wrong = "predicitons" and right = "predictions" - or - wrong = "predomiantly" and right = "predominately" - or - wrong = "prefered" and right = "preferred" - or - wrong = "prefering" and right = "preferring" - or - wrong = "preferrably" and right = "preferably" - or - wrong = "pregancies" and right = "pregnancies" - or - wrong = "preiod" and right = "period" - or - wrong = "preliferation" and right = "proliferation" - or - wrong = "premeire" and right = "premiere" - or - wrong = "premeired" and right = "premiered" - or - wrong = "premillenial" and right = "premillennial" - or - wrong = "preminence" and right = "preeminence" - or - wrong = "premission" and right = "permission" - or - wrong = "premonasterians" and right = "premonstratensians" - or - wrong = "preocupation" and right = "preoccupation" - or - wrong = "prepair" and right = "prepare" - or - wrong = "prepartion" and right = "preparation" - or - wrong = "prepatory" and right = "preparatory" - or - wrong = "preperation" and right = "preparation" - or - wrong = "preperations" and right = "preparations" - or - wrong = "preriod" and right = "period" - or - wrong = "presedential" and right = "presidential" - or - wrong = "presense" and right = "presence" - or - wrong = "presidenital" and right = "presidential" - or - wrong = "presidental" and right = "presidential" - or - wrong = "presitgious" and right = "prestigious" - or - wrong = "prespective" and right = "perspective" - or - wrong = "prestigeous" and right = "prestigious" - or - wrong = "prestigous" and right = "prestigious" - or - wrong = "presumabely" and right = "presumably" - or - wrong = "presumibly" and right = "presumably" - or - wrong = "pretection" and right = "protection" - or - wrong = "prevelant" and right = "prevalent" - or - wrong = "preverse" and right = "perverse" - or - wrong = "previvous" and right = "previous" - or - wrong = "pricipal" and right = "principal" - or - wrong = "priciple" and right = "principle" - or - wrong = "priestood" and right = "priesthood" - or - wrong = "primarly" and right = "primarily" - or - wrong = "primative" and right = "primitive" - or - wrong = "primatively" and right = "primitively" - or - wrong = "primatives" and right = "primitives" - or - wrong = "primordal" and right = "primordial" - or - wrong = "principaly" and right = "principality" - or - wrong = "principial" and right = "principal" - or - wrong = "principlaity" and right = "principality" - or - wrong = "principly" and right = "principally" - or - wrong = "prinicipal" and right = "principal" - or - wrong = "privalege" and right = "privilege" - or - wrong = "privaleges" and right = "privileges" - or - wrong = "priveledges" and right = "privileges" - or - wrong = "privelege" and right = "privilege" - or - wrong = "priveleged" and right = "privileged" - or - wrong = "priveleges" and right = "privileges" - or - wrong = "privelige" and right = "privilege" - or - wrong = "priveliged" and right = "privileged" - or - wrong = "priveliges" and right = "privileges" - or - wrong = "privelleges" and right = "privileges" - or - wrong = "privilage" and right = "privilege" - or - wrong = "priviledge" and right = "privilege" - or - wrong = "priviledges" and right = "privileges" - or - wrong = "privledge" and right = "privilege" - or - wrong = "privte" and right = "private" - or - wrong = "probabilaty" and right = "probability" - or - wrong = "probablistic" and right = "probabilistic" - or - wrong = "probablly" and right = "probably" - or - wrong = "probalibity" and right = "probability" - or - wrong = "probaly" and right = "probably" - or - wrong = "probelm" and right = "problem" - or - wrong = "proccess" and right = "process" - or - wrong = "proccessing" and right = "processing" - or - wrong = "procede" and right = "precede" - or - wrong = "procede" and right = "proceed" - or - wrong = "proceded" and right = "preceded" - or - wrong = "proceded" and right = "proceeded" - or - wrong = "procedes" and right = "precedes" - or - wrong = "procedes" and right = "proceeds" - or - wrong = "procedger" and right = "procedure" - or - wrong = "proceding" and right = "preceding" - or - wrong = "proceding" and right = "proceeding" - or - wrong = "procedings" and right = "proceedings" - or - wrong = "proceedure" and right = "procedure" - or - wrong = "proces" and right = "process" - or - wrong = "procesed" and right = "processed" - or - wrong = "processer" and right = "processor" - or - wrong = "proclaimation" and right = "proclamation" - or - wrong = "proclamed" and right = "proclaimed" - or - wrong = "proclaming" and right = "proclaiming" - or - wrong = "proclomation" and right = "proclamation" - or - wrong = "profesion" and right = "profession" - or - wrong = "profesion" and right = "profusion" - or - wrong = "profesor" and right = "professor" - or - wrong = "professer" and right = "professor" - or - wrong = "proffesed" and right = "professed" - or - wrong = "proffesion" and right = "profession" - or - wrong = "proffesional" and right = "professional" - or - wrong = "proffesor" and right = "professor" - or - wrong = "profilic" and right = "prolific" - or - wrong = "progessed" and right = "progressed" - or - wrong = "progidy" and right = "prodigy" - or - wrong = "programable" and right = "programmable" - or - wrong = "progrom" and right = "pogrom" - or - wrong = "progrom" and right = "program" - or - wrong = "progroms" and right = "pogroms" - or - wrong = "progroms" and right = "programs" - or - wrong = "prohabition" and right = "prohibition" - or - wrong = "prologomena" and right = "prolegomena" - or - wrong = "prominance" and right = "prominence" - or - wrong = "prominant" and right = "prominent" - or - wrong = "prominantly" and right = "prominently" - or - wrong = "prominately" and right = "predominately" - or - wrong = "prominately" and right = "prominently" - or - wrong = "promiscous" and right = "promiscuous" - or - wrong = "promotted" and right = "promoted" - or - wrong = "pronomial" and right = "pronominal" - or - wrong = "pronouced" and right = "pronounced" - or - wrong = "pronounched" and right = "pronounced" - or - wrong = "pronounciation" and right = "pronunciation" - or - wrong = "proove" and right = "prove" - or - wrong = "prooved" and right = "proved" - or - wrong = "prophacy" and right = "prophecy" - or - wrong = "propietary" and right = "proprietary" - or - wrong = "propmted" and right = "prompted" - or - wrong = "propoganda" and right = "propaganda" - or - wrong = "propogate" and right = "propagate" - or - wrong = "propogates" and right = "propagates" - or - wrong = "propogation" and right = "propagation" - or - wrong = "propostion" and right = "proposition" - or - wrong = "propotions" and right = "proportions" - or - wrong = "propper" and right = "proper" - or - wrong = "propperly" and right = "properly" - or - wrong = "proprietory" and right = "proprietary" - or - wrong = "proseletyzing" and right = "proselytizing" - or - wrong = "protaganist" and right = "protagonist" - or - wrong = "protaganists" and right = "protagonists" - or - wrong = "protocal" and right = "protocol" - or - wrong = "protoganist" and right = "protagonist" - or - wrong = "prototpe" and right = "prototype" - or - wrong = "protoype" and right = "prototype" - or - wrong = "protrayed" and right = "portrayed" - or - wrong = "protruberance" and right = "protuberance" - or - wrong = "protruberances" and right = "protuberances" - or - wrong = "prouncements" and right = "pronouncements" - or - wrong = "provacative" and right = "provocative" - or - wrong = "provded" and right = "provided" - or - wrong = "provicial" and right = "provincial" - or - wrong = "provinicial" and right = "provincial" - or - wrong = "provisiosn" and right = "provision" - or - wrong = "provisonal" and right = "provisional" - or - wrong = "proximty" and right = "proximity" - or - wrong = "pseudononymous" and right = "pseudonymous" - or - wrong = "pseudonyn" and right = "pseudonym" - or - wrong = "psuedo" and right = "pseudo" - or - wrong = "psycology" and right = "psychology" - or - wrong = "psyhic" and right = "psychic" - or - wrong = "pubilsher" and right = "publisher" - or - wrong = "pubisher" and right = "publisher" - or - wrong = "publiaher" and right = "publisher" - or - wrong = "publically" and right = "publicly" - or - wrong = "publicaly" and right = "publicly" - or - wrong = "publicher" and right = "publisher" - or - wrong = "publihser" and right = "publisher" - or - wrong = "publisehr" and right = "publisher" - or - wrong = "publiser" and right = "publisher" - or - wrong = "publisger" and right = "publisher" - or - wrong = "publisheed" and right = "published" - or - wrong = "publisherr" and right = "publisher" - or - wrong = "publishher" and right = "publisher" - or - wrong = "publishor" and right = "publisher" - or - wrong = "publishre" and right = "publisher" - or - wrong = "publissher" and right = "publisher" - or - wrong = "publlisher" and right = "publisher" - or - wrong = "publsiher" and right = "publisher" - or - wrong = "publusher" and right = "publisher" - or - wrong = "puchasing" and right = "purchasing" - or - wrong = "pucini" and right = "puccini" - or - wrong = "pulisher" and right = "publisher" - or - wrong = "pumkin" and right = "pumpkin" - or - wrong = "puplisher" and right = "publisher" - or - wrong = "puritannical" and right = "puritanical" - or - wrong = "purposedly" and right = "purposely" - or - wrong = "purpotedly" and right = "purportedly" - or - wrong = "pursuade" and right = "persuade" - or - wrong = "pursuaded" and right = "persuaded" - or - wrong = "pursuades" and right = "persuades" - or - wrong = "pususading" and right = "persuading" - or - wrong = "puting" and right = "putting" - or - wrong = "pwoer" and right = "power" - or - wrong = "pyscic" and right = "psychic" - or - wrong = "qtuie" and right = "quiet" - or - wrong = "qtuie" and right = "quite" - or - wrong = "quantaty" and right = "quantity" - or - wrong = "quantitiy" and right = "quantity" - or - wrong = "quarantaine" and right = "quarantine" - or - wrong = "queenland" and right = "queensland" - or - wrong = "questonable" and right = "questionable" - or - wrong = "quicklyu" and right = "quickly" - or - wrong = "quinessential" and right = "quintessential" - or - wrong = "quitted" and right = "quit" - or - wrong = "quizes" and right = "quizzes" - or - wrong = "qutie" and right = "quiet" - or - wrong = "qutie" and right = "quite" - or - wrong = "rabinnical" and right = "rabbinical" - or - wrong = "racaus" and right = "raucous" - or - wrong = "radiactive" and right = "radioactive" - or - wrong = "radify" and right = "ratify" - or - wrong = "raelly" and right = "really" - or - wrong = "rarified" and right = "rarefied" - or - wrong = "reaccurring" and right = "recurring" - or - wrong = "reacing" and right = "reaching" - or - wrong = "reacll" and right = "recall" - or - wrong = "readmition" and right = "readmission" - or - wrong = "realitvely" and right = "relatively" - or - wrong = "realsitic" and right = "realistic" - or - wrong = "realtions" and right = "relations" - or - wrong = "realy" and right = "really" - or - wrong = "realyl" and right = "really" - or - wrong = "reasearch" and right = "research" - or - wrong = "rebiulding" and right = "rebuilding" - or - wrong = "rebllions" and right = "rebellions" - or - wrong = "rebounce" and right = "rebound" - or - wrong = "reccomend" and right = "recommend" - or - wrong = "reccomendations" and right = "recommendations" - or - wrong = "reccomended" and right = "recommended" - or - wrong = "reccomending" and right = "recommending" - or - wrong = "reccommend" and right = "recommend" - or - wrong = "reccommended" and right = "recommended" - or - wrong = "reccommending" and right = "recommending" - or - wrong = "reccuring" and right = "recurring" - or - wrong = "receeded" and right = "receded" - or - wrong = "receeding" and right = "receding" - or - wrong = "recepient" and right = "recipient" - or - wrong = "recepients" and right = "recipients" - or - wrong = "receving" and right = "receiving" - or - wrong = "rechargable" and right = "rechargeable" - or - wrong = "reched" and right = "reached" - or - wrong = "recide" and right = "reside" - or - wrong = "recided" and right = "resided" - or - wrong = "recident" and right = "resident" - or - wrong = "recidents" and right = "residents" - or - wrong = "reciding" and right = "residing" - or - wrong = "reciepents" and right = "recipients" - or - wrong = "reciept" and right = "receipt" - or - wrong = "recieve" and right = "receive" - or - wrong = "recieved" and right = "received" - or - wrong = "reciever" and right = "receiver" - or - wrong = "recievers" and right = "receivers" - or - wrong = "recieves" and right = "receives" - or - wrong = "recieving" and right = "receiving" - or - wrong = "recipiant" and right = "recipient" - or - wrong = "recipiants" and right = "recipients" - or - wrong = "recived" and right = "received" - or - wrong = "recivership" and right = "receivership" - or - wrong = "recogise" and right = "recognise" - or - wrong = "recogize" and right = "recognize" - or - wrong = "recomend" and right = "recommend" - or - wrong = "recomended" and right = "recommended" - or - wrong = "recomending" and right = "recommending" - or - wrong = "recomends" and right = "recommends" - or - wrong = "recommedations" and right = "recommendations" - or - wrong = "recompence" and right = "recompense" - or - wrong = "reconaissance" and right = "reconnaissance" - or - wrong = "reconcilation" and right = "reconciliation" - or - wrong = "reconized" and right = "recognized" - or - wrong = "reconnaisance" and right = "reconnaissance" - or - wrong = "reconnaissence" and right = "reconnaissance" - or - wrong = "recontructed" and right = "reconstructed" - or - wrong = "recquired" and right = "required" - or - wrong = "recrational" and right = "recreational" - or - wrong = "recrod" and right = "record" - or - wrong = "recuiting" and right = "recruiting" - or - wrong = "recuring" and right = "recurring" - or - wrong = "recurrance" and right = "recurrence" - or - wrong = "rediculous" and right = "ridiculous" - or - wrong = "reedeming" and right = "redeeming" - or - wrong = "reenforced" and right = "reinforced" - or - wrong = "refect" and right = "reflect" - or - wrong = "refedendum" and right = "referendum" - or - wrong = "referal" and right = "referral" - or - wrong = "referece" and right = "reference" - or - wrong = "refereces" and right = "references" - or - wrong = "refered" and right = "referred" - or - wrong = "referemce" and right = "reference" - or - wrong = "referemces" and right = "references" - or - wrong = "referencs" and right = "references" - or - wrong = "referenece" and right = "reference" - or - wrong = "refereneced" and right = "referenced" - or - wrong = "refereneces" and right = "references" - or - wrong = "referiang" and right = "referring" - or - wrong = "refering" and right = "referring" - or - wrong = "refernce" and right = "reference" - or - wrong = "refernce" and right = "references" - or - wrong = "refernces" and right = "references" - or - wrong = "referrence" and right = "reference" - or - wrong = "referrences" and right = "references" - or - wrong = "referrs" and right = "refers" - or - wrong = "reffered" and right = "referred" - or - wrong = "refference" and right = "reference" - or - wrong = "reffering" and right = "referring" - or - wrong = "refrence" and right = "reference" - or - wrong = "refrences" and right = "references" - or - wrong = "refrers" and right = "refers" - or - wrong = "refridgeration" and right = "refrigeration" - or - wrong = "refridgerator" and right = "refrigerator" - or - wrong = "refromist" and right = "reformist" - or - wrong = "refusla" and right = "refusal" - or - wrong = "regardes" and right = "regards" - or - wrong = "regluar" and right = "regular" - or - wrong = "reguarly" and right = "regularly" - or - wrong = "regulaion" and right = "regulation" - or - wrong = "regulaotrs" and right = "regulators" - or - wrong = "regularily" and right = "regularly" - or - wrong = "rehersal" and right = "rehearsal" - or - wrong = "reicarnation" and right = "reincarnation" - or - wrong = "reigining" and right = "reigning" - or - wrong = "reknown" and right = "renown" - or - wrong = "reknowned" and right = "renowned" - or - wrong = "rela" and right = "real" - or - wrong = "relaly" and right = "really" - or - wrong = "relatiopnship" and right = "relationship" - or - wrong = "relativly" and right = "relatively" - or - wrong = "relected" and right = "reelected" - or - wrong = "releive" and right = "relieve" - or - wrong = "releived" and right = "relieved" - or - wrong = "releiver" and right = "reliever" - or - wrong = "releses" and right = "releases" - or - wrong = "relevence" and right = "relevance" - or - wrong = "relevent" and right = "relevant" - or - wrong = "reliablity" and right = "reliability" - or - wrong = "relient" and right = "reliant" - or - wrong = "religeous" and right = "religious" - or - wrong = "religous" and right = "religious" - or - wrong = "religously" and right = "religiously" - or - wrong = "relinqushment" and right = "relinquishment" - or - wrong = "relitavely" and right = "relatively" - or - wrong = "relized" and right = "realised" - or - wrong = "relized" and right = "realized" - or - wrong = "relpacement" and right = "replacement" - or - wrong = "remaing" and right = "remaining" - or - wrong = "remeber" and right = "remember" - or - wrong = "rememberable" and right = "memorable" - or - wrong = "rememberance" and right = "remembrance" - or - wrong = "remembrence" and right = "remembrance" - or - wrong = "remenant" and right = "remnant" - or - wrong = "remenicent" and right = "reminiscent" - or - wrong = "reminent" and right = "remnant" - or - wrong = "reminescent" and right = "reminiscent" - or - wrong = "reminscent" and right = "reminiscent" - or - wrong = "reminsicent" and right = "reminiscent" - or - wrong = "rendevous" and right = "rendezvous" - or - wrong = "rendezous" and right = "rendezvous" - or - wrong = "renedered" and right = "rende" - or - wrong = "renewl" and right = "renewal" - or - wrong = "rennovate" and right = "renovate" - or - wrong = "rennovated" and right = "renovated" - or - wrong = "rennovating" and right = "renovating" - or - wrong = "rennovation" and right = "renovation" - or - wrong = "rentors" and right = "renters" - or - wrong = "reoccurrence" and right = "recurrence" - or - wrong = "reorganision" and right = "reorganisation" - or - wrong = "repatition" and right = "repartition" - or - wrong = "repatition" and right = "repetition" - or - wrong = "repblic" and right = "republic" - or - wrong = "repblican" and right = "republican" - or - wrong = "repblicans" and right = "republicans" - or - wrong = "repblics" and right = "republics" - or - wrong = "repectively" and right = "respectively" - or - wrong = "repeition" and right = "repetition" - or - wrong = "repentence" and right = "repentance" - or - wrong = "repentent" and right = "repentant" - or - wrong = "repeteadly" and right = "repeatedly" - or - wrong = "repetion" and right = "repetition" - or - wrong = "repid" and right = "rapid" - or - wrong = "reponse" and right = "response" - or - wrong = "reponsible" and right = "responsible" - or - wrong = "reportadly" and right = "reportedly" - or - wrong = "represantative" and right = "representative" - or - wrong = "representive" and right = "representative" - or - wrong = "representives" and right = "representatives" - or - wrong = "reproducable" and right = "reproducible" - or - wrong = "reprtoire" and right = "repertoire" - or - wrong = "repsectively" and right = "respectively" - or - wrong = "reptition" and right = "repetition" - or - wrong = "repubic" and right = "republic" - or - wrong = "repubican" and right = "republican" - or - wrong = "repubicans" and right = "republicans" - or - wrong = "repubics" and right = "republics" - or - wrong = "republi" and right = "republic" - or - wrong = "republian" and right = "republican" - or - wrong = "republians" and right = "republicans" - or - wrong = "republis" and right = "republics" - or - wrong = "repulic" and right = "republic" - or - wrong = "repulican" and right = "republican" - or - wrong = "repulicans" and right = "republicans" - or - wrong = "repulics" and right = "republics" - or - wrong = "requirment" and right = "requirement" - or - wrong = "requred" and right = "required" - or - wrong = "resaurant" and right = "restaurant" - or - wrong = "resembelance" and right = "resemblance" - or - wrong = "resembes" and right = "resembles" - or - wrong = "resemblence" and right = "resemblance" - or - wrong = "resevoir" and right = "reservoir" - or - wrong = "residental" and right = "residential" - or - wrong = "resignement" and right = "resignment" - or - wrong = "resistable" and right = "resistible" - or - wrong = "resistence" and right = "resistance" - or - wrong = "resistent" and right = "resistant" - or - wrong = "respectivly" and right = "respectively" - or - wrong = "responce" and right = "response" - or - wrong = "responibilities" and right = "responsibilities" - or - wrong = "responisble" and right = "responsible" - or - wrong = "responnsibilty" and right = "responsibility" - or - wrong = "responsability" and right = "responsibility" - or - wrong = "responsibile" and right = "responsible" - or - wrong = "responsibilites" and right = "responsibilities" - or - wrong = "responsiblities" and right = "responsibilities" - or - wrong = "responsiblity" and right = "responsibility" - or - wrong = "ressemblance" and right = "resemblance" - or - wrong = "ressemble" and right = "resemble" - or - wrong = "ressembled" and right = "resembled" - or - wrong = "ressemblence" and right = "resemblance" - or - wrong = "ressembling" and right = "resembling" - or - wrong = "resssurecting" and right = "resurrecting" - or - wrong = "ressurect" and right = "resurrect" - or - wrong = "ressurected" and right = "resurrected" - or - wrong = "ressurection" and right = "resurrection" - or - wrong = "ressurrection" and right = "resurrection" - or - wrong = "restarant" and right = "restaurant" - or - wrong = "restarants" and right = "restaurants" - or - wrong = "restaraunt" and right = "restaurant" - or - wrong = "restaraunteur" and right = "restaurateur" - or - wrong = "restaraunteurs" and right = "restaurateurs" - or - wrong = "restaraunts" and right = "restaurants" - or - wrong = "restauranteurs" and right = "restaurateurs" - or - wrong = "restauration" and right = "restoration" - or - wrong = "restauraunt" and right = "restaurant" - or - wrong = "resteraunt" and right = "restaurant" - or - wrong = "resteraunts" and right = "restaurants" - or - wrong = "resticted" and right = "restricted" - or - wrong = "restraunt" and right = "restaurant" - or - wrong = "restraunt" and right = "restraint" - or - wrong = "resturant" and right = "restaurant" - or - wrong = "resturants" and right = "restaurants" - or - wrong = "resturaunt" and right = "restaurant" - or - wrong = "resturaunts" and right = "restaurants" - or - wrong = "resurecting" and right = "resurrecting" - or - wrong = "retalitated" and right = "retaliated" - or - wrong = "retalitation" and right = "retaliation" - or - wrong = "retreive" and right = "retrieve" - or - wrong = "retrive" and right = "retrieve" - or - wrong = "returnd" and right = "returned" - or - wrong = "revaluated" and right = "reevaluated" - or - wrong = "reveiw" and right = "review" - or - wrong = "reveral" and right = "reversal" - or - wrong = "reversable" and right = "reversible" - or - wrong = "revolutionar" and right = "revolutionary" - or - wrong = "rewitten" and right = "rewritten" - or - wrong = "rewriet" and right = "rewrite" - or - wrong = "rference" and right = "reference" - or - wrong = "rferences" and right = "references" - or - wrong = "rhymme" and right = "rhyme" - or - wrong = "rhythem" and right = "rhythm" - or - wrong = "rhythim" and right = "rhythm" - or - wrong = "rhytmic" and right = "rhythmic" - or - wrong = "rigeur" and right = "rigor" - or - wrong = "rigeur" and right = "rigour" - or - wrong = "rigeur" and right = "rigueur" - or - wrong = "rigourous" and right = "rigorous" - or - wrong = "rininging" and right = "ringing" - or - wrong = "rised" and right = "raised" - or - wrong = "rised" and right = "rose" - or - wrong = "rockerfeller" and right = "rockefeller" - or - wrong = "rococco" and right = "rococo" - or - wrong = "rocord" and right = "record" - or - wrong = "roomate" and right = "roommate" - or - wrong = "rougly" and right = "roughly" - or - wrong = "rucuperate" and right = "recuperate" - or - wrong = "rudimentatry" and right = "rudimentary" - or - wrong = "rulle" and right = "rule" - or - wrong = "runing" and right = "running" - or - wrong = "runnung" and right = "running" - or - wrong = "russina" and right = "russian" - or - wrong = "russion" and right = "russian" - or - wrong = "rwite" and right = "write" - or - wrong = "rythem" and right = "rhythm" - or - wrong = "rythim" and right = "rhythm" - or - wrong = "rythm" and right = "rhythm" - or - wrong = "rythmic" and right = "rhythmic" - or - wrong = "rythyms" and right = "rhythms" - or - wrong = "sacrafice" and right = "sacrifice" - or - wrong = "sacreligious" and right = "sacrilegious" - or - wrong = "sacremento" and right = "sacramento" - or - wrong = "sacrifical" and right = "sacrificial" - or - wrong = "saftey" and right = "safety" - or - wrong = "safty" and right = "safety" - or - wrong = "salery" and right = "salary" - or - wrong = "sanctionning" and right = "sanctioning" - or - wrong = "sandwhich" and right = "sandwich" - or - wrong = "sanhedrim" and right = "sanhedrin" - or - wrong = "santioned" and right = "sanctioned" - or - wrong = "sargant" and right = "sergeant" - or - wrong = "sargeant" and right = "sergeant" - or - wrong = "sasy" and right = "sassy" - or - wrong = "sasy" and right = "says" - or - wrong = "satelite" and right = "satellite" - or - wrong = "satelites" and right = "satellites" - or - wrong = "saterday" and right = "saturday" - or - wrong = "saterdays" and right = "saturdays" - or - wrong = "satisfactority" and right = "satisfactorily" - or - wrong = "satric" and right = "satiric" - or - wrong = "satrical" and right = "satirical" - or - wrong = "satrically" and right = "satirically" - or - wrong = "sattelite" and right = "satellite" - or - wrong = "sattelites" and right = "satellites" - or - wrong = "saught" and right = "sought" - or - wrong = "saveing" and right = "saving" - or - wrong = "saxaphone" and right = "saxophone" - or - wrong = "scaleable" and right = "scalable" - or - wrong = "scandanavia" and right = "scandinavia" - or - wrong = "scaricity" and right = "scarcity" - or - wrong = "scavanged" and right = "scavenged" - or - wrong = "schedual" and right = "schedule" - or - wrong = "scholarhip" and right = "scholarship" - or - wrong = "scholarstic" and right = "scholarly" - or - wrong = "scholarstic" and right = "scholastic" - or - wrong = "scientfic" and right = "scientific" - or - wrong = "scientifc" and right = "scientific" - or - wrong = "scientis" and right = "scientist" - or - wrong = "scince" and right = "science" - or - wrong = "scinece" and right = "science" - or - wrong = "scirpt" and right = "script" - or - wrong = "scoll" and right = "scroll" - or - wrong = "screenwrighter" and right = "screenwriter" - or - wrong = "scrutinity" and right = "scrutiny" - or - wrong = "scuptures" and right = "sculptures" - or - wrong = "seach" and right = "search" - or - wrong = "seached" and right = "searched" - or - wrong = "seaches" and right = "searches" - or - wrong = "secceeded" and right = "seceded" - or - wrong = "secceeded" and right = "succeeded" - or - wrong = "seceed" and right = "secede" - or - wrong = "seceed" and right = "succeed" - or - wrong = "seceeded" and right = "seceded" - or - wrong = "seceeded" and right = "succeeded" - or - wrong = "secratary" and right = "secretary" - or - wrong = "secretery" and right = "secretary" - or - wrong = "sedereal" and right = "sidereal" - or - wrong = "seeked" and right = "sought" - or - wrong = "segementation" and right = "segmentation" - or - wrong = "seguoys" and right = "segues" - or - wrong = "seige" and right = "siege" - or - wrong = "seing" and right = "seeing" - or - wrong = "seinor" and right = "senior" - or - wrong = "seldomly" and right = "seldom" - or - wrong = "senarios" and right = "scenarios" - or - wrong = "sence" and right = "sense" - or - wrong = "sence" and right = "since" - or - wrong = "senstive" and right = "sensitive" - or - wrong = "sensure" and right = "censure" - or - wrong = "seperate" and right = "separate" - or - wrong = "seperated" and right = "separated" - or - wrong = "seperately" and right = "separately" - or - wrong = "seperates" and right = "separates" - or - wrong = "seperating" and right = "separating" - or - wrong = "seperation" and right = "separation" - or - wrong = "seperatism" and right = "separatism" - or - wrong = "seperatist" and right = "separatist" - or - wrong = "seperator" and right = "separator" - or - wrong = "sepina" and right = "subpoena" - or - wrong = "sepulchure" and right = "sepulcher" - or - wrong = "sepulchure" and right = "sepulchre" - or - wrong = "sepulcre" and right = "sepulcher" - or - wrong = "sepulcre" and right = "sepulchre" - or - wrong = "sergent" and right = "sergeant" - or - wrong = "settelement" and right = "settlement" - or - wrong = "settlment" and right = "settlement" - or - wrong = "settting" and right = "setting" - or - wrong = "severeal" and right = "several" - or - wrong = "severley" and right = "severely" - or - wrong = "severly" and right = "severely" - or - wrong = "sevice" and right = "service" - or - wrong = "shadasloo" and right = "shadaloo" - or - wrong = "shaddow" and right = "shadow" - or - wrong = "shadoloo" and right = "shadaloo" - or - wrong = "shamen" and right = "shaman" - or - wrong = "shamen" and right = "shamans" - or - wrong = "sheat" and right = "cheat" - or - wrong = "sheat" and right = "sheath" - or - wrong = "sheat" and right = "sheet" - or - wrong = "sheild" and right = "shield" - or - wrong = "sherif" and right = "sheriff" - or - wrong = "shineing" and right = "shining" - or - wrong = "shiped" and right = "shipped" - or - wrong = "shiping" and right = "shipping" - or - wrong = "shopkeeepers" and right = "shopkeepers" - or - wrong = "shorly" and right = "shortly" - or - wrong = "shoudl" and right = "should" - or - wrong = "shoudln" and right = "should" - or - wrong = "shreak" and right = "shriek" - or - wrong = "shrinked" and right = "shrunk" - or - wrong = "sicne" and right = "since" - or - wrong = "sideral" and right = "sidereal" - or - wrong = "sieze" and right = "seize" - or - wrong = "sieze" and right = "size" - or - wrong = "siezed" and right = "seized" - or - wrong = "siezed" and right = "sized" - or - wrong = "siezing" and right = "seizing" - or - wrong = "siezing" and right = "sizing" - or - wrong = "siezure" and right = "seizure" - or - wrong = "siezures" and right = "seizures" - or - wrong = "siginificant" and right = "significant" - or - wrong = "signficant" and right = "significant" - or - wrong = "signficiant" and right = "significant" - or - wrong = "signfies" and right = "signifies" - or - wrong = "signifantly" and right = "significantly" - or - wrong = "significently" and right = "significantly" - or - wrong = "signifigant" and right = "significant" - or - wrong = "signifigantly" and right = "significantly" - or - wrong = "signitories" and right = "signatories" - or - wrong = "signitory" and right = "signatory" - or - wrong = "similarily" and right = "similarly" - or - wrong = "similiar" and right = "similar" - or - wrong = "similiarity" and right = "similarity" - or - wrong = "similiarly" and right = "similarly" - or - wrong = "simmilar" and right = "similar" - or - wrong = "simpley" and right = "simply" - or - wrong = "simplier" and right = "simpler" - or - wrong = "simultanous" and right = "simultaneous" - or - wrong = "simultanously" and right = "simultaneously" - or - wrong = "sincerley" and right = "sincerely" - or - wrong = "singsog" and right = "singsong" - or - wrong = "sinse" and right = "since" - or - wrong = "sinse" and right = "sines" - or - wrong = "sionist" and right = "zionist" - or - wrong = "sionists" and right = "zionists" - or - wrong = "sixtin" and right = "sistine" - or - wrong = "skagerak" and right = "skagerrak" - or - wrong = "skateing" and right = "skating" - or - wrong = "slaugterhouses" and right = "slaughterhouses" - or - wrong = "slighly" and right = "slightly" - or - wrong = "slippy" and right = "slippery" - or - wrong = "slowy" and right = "slowly" - or - wrong = "smae" and right = "same" - or - wrong = "smealting" and right = "smelting" - or - wrong = "smoe" and right = "some" - or - wrong = "sneeks" and right = "sneaks" - or - wrong = "snese" and right = "sneeze" - or - wrong = "socalism" and right = "socialism" - or - wrong = "socities" and right = "societies" - or - wrong = "soem" and right = "some" - or - wrong = "sofware" and right = "software" - or - wrong = "sohw" and right = "show" - or - wrong = "soilders" and right = "soldiers" - or - wrong = "solatary" and right = "solitary" - or - wrong = "soley" and right = "solely" - or - wrong = "soliders" and right = "soldiers" - or - wrong = "soliliquy" and right = "soliloquy" - or - wrong = "soluable" and right = "soluble" - or - wrong = "somene" and right = "someone" - or - wrong = "somtimes" and right = "sometimes" - or - wrong = "somwhere" and right = "somewhere" - or - wrong = "sophicated" and right = "sophisticated" - or - wrong = "sophmore" and right = "sophomore" - or - wrong = "sorceror" and right = "sorcerer" - or - wrong = "sorrounding" and right = "surrounding" - or - wrong = "sotry" and right = "story" - or - wrong = "sotyr" and right = "satyr" - or - wrong = "sotyr" and right = "story" - or - wrong = "soudn" and right = "sound" - or - wrong = "soudns" and right = "sounds" - or - wrong = "sould" and right = "could" - or - wrong = "sould" and right = "should" - or - wrong = "sould" and right = "sold" - or - wrong = "sould" and right = "soul" - or - wrong = "sountrack" and right = "soundtrack" - or - wrong = "sourth" and right = "south" - or - wrong = "sourthern" and right = "southern" - or - wrong = "souvenier" and right = "souvenir" - or - wrong = "souveniers" and right = "souvenirs" - or - wrong = "soveits" and right = "soviets" - or - wrong = "sovereignity" and right = "sovereignty" - or - wrong = "soverign" and right = "sovereign" - or - wrong = "soverignity" and right = "sovereignty" - or - wrong = "soverignty" and right = "sovereignty" - or - wrong = "spainish" and right = "spanish" - or - wrong = "speach" and right = "speech" - or - wrong = "specfic" and right = "specific" - or - wrong = "speciallized" and right = "specialised" - or - wrong = "speciallized" and right = "specialized" - or - wrong = "specif" and right = "specific" - or - wrong = "specif" and right = "specify" - or - wrong = "specifiying" and right = "specifying" - or - wrong = "speciman" and right = "specimen" - or - wrong = "spectauclar" and right = "spectacular" - or - wrong = "spectaulars" and right = "spectaculars" - or - wrong = "spects" and right = "aspects" - or - wrong = "spects" and right = "expects" - or - wrong = "spectum" and right = "spectrum" - or - wrong = "speices" and right = "species" - or - wrong = "spendour" and right = "splendour" - or - wrong = "spermatozoan" and right = "spermatozoon" - or - wrong = "spoace" and right = "space" - or - wrong = "sponser" and right = "sponsor" - or - wrong = "sponsered" and right = "sponsored" - or - wrong = "spontanous" and right = "spontaneous" - or - wrong = "sponzored" and right = "sponsored" - or - wrong = "spoonfulls" and right = "spoonfuls" - or - wrong = "sppeches" and right = "speeches" - or - wrong = "spreaded" and right = "spread" - or - wrong = "sprech" and right = "speech" - or - wrong = "spred" and right = "spread" - or - wrong = "spriritual" and right = "spiritual" - or - wrong = "spritual" and right = "spiritual" - or - wrong = "sqaure" and right = "square" - or - wrong = "sring" and right = "string" - or - wrong = "stablility" and right = "stability" - or - wrong = "stainlees" and right = "stainless" - or - wrong = "staion" and right = "station" - or - wrong = "standars" and right = "standards" - or - wrong = "stange" and right = "strange" - or - wrong = "startegic" and right = "strategic" - or - wrong = "startegies" and right = "strategies" - or - wrong = "startegy" and right = "strategy" - or - wrong = "stateman" and right = "statesman" - or - wrong = "statememts" and right = "statements" - or - wrong = "statment" and right = "statement" - or - wrong = "steriods" and right = "steroids" - or - wrong = "sterotypes" and right = "stereotypes" - or - wrong = "stilus" and right = "stylus" - or - wrong = "stingent" and right = "stringent" - or - wrong = "stiring" and right = "stirring" - or - wrong = "stirrs" and right = "stirs" - or - wrong = "stlye" and right = "style" - or - wrong = "stomache" and right = "stomach" - or - wrong = "stong" and right = "strong" - or - wrong = "stopry" and right = "story" - or - wrong = "storeis" and right = "stories" - or - wrong = "storise" and right = "stories" - or - wrong = "stornegst" and right = "strongest" - or - wrong = "stoyr" and right = "story" - or - wrong = "stpo" and right = "stop" - or - wrong = "stradegies" and right = "strategies" - or - wrong = "stradegy" and right = "strategy" - or - wrong = "strat" and right = "start" - or - wrong = "strat" and right = "strata" - or - wrong = "stratagically" and right = "strategically" - or - wrong = "streemlining" and right = "streamlining" - or - wrong = "stregth" and right = "strength" - or - wrong = "strenghen" and right = "strengthen" - or - wrong = "strenghened" and right = "strengthened" - or - wrong = "strenghening" and right = "strengthening" - or - wrong = "strenght" and right = "strength" - or - wrong = "strenghten" and right = "strengthen" - or - wrong = "strenghtened" and right = "strengthened" - or - wrong = "strenghtening" and right = "strengthening" - or - wrong = "strengtened" and right = "strengthened" - or - wrong = "strenous" and right = "strenuous" - or - wrong = "strictist" and right = "strictest" - or - wrong = "strikely" and right = "strikingly" - or - wrong = "strnad" and right = "strand" - or - wrong = "stroy" and right = "destroy" - or - wrong = "stroy" and right = "story" - or - wrong = "structual" and right = "structural" - or - wrong = "stubborness" and right = "stubbornness" - or - wrong = "stucture" and right = "structure" - or - wrong = "stuctured" and right = "structured" - or - wrong = "studdy" and right = "study" - or - wrong = "studing" and right = "studying" - or - wrong = "stuggling" and right = "struggling" - or - wrong = "sturcture" and right = "structure" - or - wrong = "subcatagories" and right = "subcategories" - or - wrong = "subcatagory" and right = "subcategory" - or - wrong = "subconsiously" and right = "subconsciously" - or - wrong = "subjudgation" and right = "subjugation" - or - wrong = "submachne" and right = "submachine" - or - wrong = "subpecies" and right = "subspecies" - or - wrong = "subsidary" and right = "subsidiary" - or - wrong = "subsiduary" and right = "subsidiary" - or - wrong = "subsquent" and right = "subsequent" - or - wrong = "subsquently" and right = "subsequently" - or - wrong = "substace" and right = "substance" - or - wrong = "substancial" and right = "substantial" - or - wrong = "substatial" and right = "substantial" - or - wrong = "substituded" and right = "substituted" - or - wrong = "substract" and right = "subtract" - or - wrong = "substracted" and right = "subtracted" - or - wrong = "substracting" and right = "subtracting" - or - wrong = "substraction" and right = "subtraction" - or - wrong = "substracts" and right = "subtracts" - or - wrong = "subtances" and right = "substances" - or - wrong = "subterranian" and right = "subterranean" - or - wrong = "suburburban" and right = "suburban" - or - wrong = "succceeded" and right = "succeeded" - or - wrong = "succcesses" and right = "successes" - or - wrong = "succedded" and right = "succeeded" - or - wrong = "succeded" and right = "succeeded" - or - wrong = "succeds" and right = "succeeds" - or - wrong = "succesful" and right = "successful" - or - wrong = "succesfully" and right = "successfully" - or - wrong = "succesfuly" and right = "successfully" - or - wrong = "succesion" and right = "succession" - or - wrong = "succesive" and right = "successive" - or - wrong = "successfull" and right = "successful" - or - wrong = "successully" and right = "successfully" - or - wrong = "succsess" and right = "success" - or - wrong = "succsessfull" and right = "successful" - or - wrong = "suceed" and right = "succeed" - or - wrong = "suceeded" and right = "succeeded" - or - wrong = "suceeding" and right = "succeeding" - or - wrong = "suceeds" and right = "succeeds" - or - wrong = "sucesful" and right = "successful" - or - wrong = "sucesfully" and right = "successfully" - or - wrong = "sucesfuly" and right = "successfully" - or - wrong = "sucesion" and right = "succession" - or - wrong = "sucess" and right = "success" - or - wrong = "sucesses" and right = "successes" - or - wrong = "sucessful" and right = "successful" - or - wrong = "sucessfull" and right = "successful" - or - wrong = "sucessfully" and right = "successfully" - or - wrong = "sucessfuly" and right = "successfully" - or - wrong = "sucession" and right = "succession" - or - wrong = "sucessive" and right = "successive" - or - wrong = "sucessor" and right = "successor" - or - wrong = "sucessot" and right = "successor" - or - wrong = "sucide" and right = "suicide" - or - wrong = "sucidial" and right = "suicidal" - or - wrong = "sudent" and right = "student" - or - wrong = "sudents" and right = "students" - or - wrong = "sufferage" and right = "suffrage" - or - wrong = "sufferred" and right = "suffered" - or - wrong = "sufferring" and right = "suffering" - or - wrong = "sufficent" and right = "sufficient" - or - wrong = "sufficently" and right = "sufficiently" - or - wrong = "sumary" and right = "summary" - or - wrong = "sunglases" and right = "sunglasses" - or - wrong = "suop" and right = "soup" - or - wrong = "superceeded" and right = "superseded" - or - wrong = "superintendant" and right = "superintendent" - or - wrong = "suphisticated" and right = "sophisticated" - or - wrong = "suplimented" and right = "supplemented" - or - wrong = "suported" and right = "supported" - or - wrong = "supose" and right = "suppose" - or - wrong = "suposed" and right = "supposed" - or - wrong = "suposedly" and right = "supposedly" - or - wrong = "suposes" and right = "supposes" - or - wrong = "suposing" and right = "supposing" - or - wrong = "supplamented" and right = "supplemented" - or - wrong = "suppliementing" and right = "supplementing" - or - wrong = "suppoed" and right = "supposed" - or - wrong = "supposingly" and right = "supposedly" - or - wrong = "suppy" and right = "supply" - or - wrong = "suprassing" and right = "surpassing" - or - wrong = "supress" and right = "suppress" - or - wrong = "supressed" and right = "suppressed" - or - wrong = "supresses" and right = "suppresses" - or - wrong = "supressing" and right = "suppressing" - or - wrong = "suprise" and right = "surprise" - or - wrong = "suprised" and right = "surprised" - or - wrong = "suprising" and right = "surprising" - or - wrong = "suprisingly" and right = "surprisingly" - or - wrong = "suprize" and right = "surprise" - or - wrong = "suprized" and right = "surprised" - or - wrong = "suprizing" and right = "surprising" - or - wrong = "suprizingly" and right = "surprisingly" - or - wrong = "surfce" and right = "surface" - or - wrong = "surley" and right = "surely" - or - wrong = "surley" and right = "surly" - or - wrong = "suround" and right = "surround" - or - wrong = "surounded" and right = "surrounded" - or - wrong = "surounding" and right = "surrounding" - or - wrong = "suroundings" and right = "surroundings" - or - wrong = "surounds" and right = "surrounds" - or - wrong = "surplanted" and right = "supplanted" - or - wrong = "surpress" and right = "suppress" - or - wrong = "surpressed" and right = "suppressed" - or - wrong = "surprize" and right = "surprise" - or - wrong = "surprized" and right = "surprised" - or - wrong = "surprizing" and right = "surprising" - or - wrong = "surprizingly" and right = "surprisingly" - or - wrong = "surrended" and right = "surrendered" - or - wrong = "surrended" and right = "surrounded" - or - wrong = "surrepetitious" and right = "surreptitious" - or - wrong = "surrepetitiously" and right = "surreptitiously" - or - wrong = "surreptious" and right = "surreptitious" - or - wrong = "surreptiously" and right = "surreptitiously" - or - wrong = "surronded" and right = "surrounded" - or - wrong = "surrouded" and right = "surrounded" - or - wrong = "surrouding" and right = "surrounding" - or - wrong = "surrundering" and right = "surrendering" - or - wrong = "surveilence" and right = "surveillance" - or - wrong = "surveill" and right = "surveil" - or - wrong = "surveyer" and right = "surveyor" - or - wrong = "surviver" and right = "survivor" - or - wrong = "survivers" and right = "survivors" - or - wrong = "survivied" and right = "survived" - or - wrong = "suseptable" and right = "susceptible" - or - wrong = "suseptible" and right = "susceptible" - or - wrong = "suspention" and right = "suspension" - or - wrong = "swaer" and right = "swear" - or - wrong = "swaers" and right = "swears" - or - wrong = "swepth" and right = "swept" - or - wrong = "swiming" and right = "swimming" - or - wrong = "syas" and right = "says" - or - wrong = "symetrical" and right = "symmetrical" - or - wrong = "symetrically" and right = "symmetrically" - or - wrong = "symetry" and right = "symmetry" - or - wrong = "symettric" and right = "symmetric" - or - wrong = "symmetral" and right = "symmetric" - or - wrong = "symmetricaly" and right = "symmetrically" - or - wrong = "synagouge" and right = "synagogue" - or - wrong = "syncronization" and right = "synchronization" - or - wrong = "synonomous" and right = "synonymous" - or - wrong = "synonymns" and right = "synonyms" - or - wrong = "synphony" and right = "symphony" - or - wrong = "syphyllis" and right = "syphilis" - or - wrong = "sypmtoms" and right = "symptoms" - or - wrong = "syrap" and right = "syrup" - or - wrong = "sysmatically" and right = "systematically" - or - wrong = "sytem" and right = "system" - or - wrong = "sytle" and right = "style" - or - wrong = "tabacco" and right = "tobacco" - or - wrong = "tahn" and right = "than" - or - wrong = "taht" and right = "that" - or - wrong = "talekd" and right = "talked" - or - wrong = "targetted" and right = "targeted" - or - wrong = "targetting" and right = "targeting" - or - wrong = "tast" and right = "taste" - or - wrong = "tath" and right = "that" - or - wrong = "tatoo" and right = "tattoo" - or - wrong = "tattooes" and right = "tattoos" - or - wrong = "taxanomic" and right = "taxonomic" - or - wrong = "taxanomy" and right = "taxonomy" - or - wrong = "teached" and right = "taught" - or - wrong = "techician" and right = "technician" - or - wrong = "techicians" and right = "technicians" - or - wrong = "techiniques" and right = "techniques" - or - wrong = "technitian" and right = "technician" - or - wrong = "technnology" and right = "technology" - or - wrong = "technolgy" and right = "technology" - or - wrong = "teh" and right = "the" - or - wrong = "tehy" and right = "they" - or - wrong = "telelevision" and right = "television" - or - wrong = "televsion" and right = "television" - or - wrong = "telphony" and right = "telephony" - or - wrong = "temerature" and right = "temperature" - or - wrong = "tempalte" and right = "template" - or - wrong = "tempaltes" and right = "templates" - or - wrong = "temparate" and right = "temperate" - or - wrong = "temperarily" and right = "temporarily" - or - wrong = "temperment" and right = "temperament" - or - wrong = "tempertaure" and right = "temperature" - or - wrong = "temperture" and right = "temperature" - or - wrong = "temprary" and right = "temporary" - or - wrong = "tenacle" and right = "tentacle" - or - wrong = "tenacles" and right = "tentacles" - or - wrong = "tendacy" and right = "tendency" - or - wrong = "tendancies" and right = "tendencies" - or - wrong = "tendancy" and right = "tendency" - or - wrong = "tepmorarily" and right = "temporarily" - or - wrong = "terrestial" and right = "terrestrial" - or - wrong = "terriories" and right = "territories" - or - wrong = "terriory" and right = "territory" - or - wrong = "territorist" and right = "terrorist" - or - wrong = "territoy" and right = "territory" - or - wrong = "terroist" and right = "terrorist" - or - wrong = "testiclular" and right = "testicular" - or - wrong = "testomony" and right = "testimony" - or - wrong = "tghe" and right = "the" - or - wrong = "thast" and right = "that" - or - wrong = "theather" and right = "theater" - or - wrong = "theese" and right = "these" - or - wrong = "theif" and right = "thief" - or - wrong = "theives" and right = "thieves" - or - wrong = "themselfs" and right = "themselves" - or - wrong = "themslves" and right = "themselves" - or - wrong = "ther" and right = "the" - or - wrong = "ther" and right = "their" - or - wrong = "ther" and right = "there" - or - wrong = "therafter" and right = "thereafter" - or - wrong = "therby" and right = "thereby" - or - wrong = "theri" and right = "their" - or - wrong = "thgat" and right = "that" - or - wrong = "thge" and right = "the" - or - wrong = "thier" and right = "their" - or - wrong = "thign" and right = "thing" - or - wrong = "thigns" and right = "things" - or - wrong = "thigsn" and right = "things" - or - wrong = "thikn" and right = "think" - or - wrong = "thikning" and right = "thickening" - or - wrong = "thikning" and right = "thinking" - or - wrong = "thikns" and right = "thinks" - or - wrong = "thiunk" and right = "think" - or - wrong = "thn" and right = "then" - or - wrong = "thna" and right = "than" - or - wrong = "thne" and right = "then" - or - wrong = "thnig" and right = "thing" - or - wrong = "thnigs" and right = "things" - or - wrong = "thoughout" and right = "throughout" - or - wrong = "threatend" and right = "threatened" - or - wrong = "threatning" and right = "threatening" - or - wrong = "threee" and right = "three" - or - wrong = "threshhold" and right = "threshold" - or - wrong = "thrid" and right = "third" - or - wrong = "throrough" and right = "thorough" - or - wrong = "throughly" and right = "thoroughly" - or - wrong = "throught" and right = "thought" - or - wrong = "throught" and right = "through" - or - wrong = "throught" and right = "throughout" - or - wrong = "througout" and right = "throughout" - or - wrong = "thru" and right = "through" - or - wrong = "thsi" and right = "this" - or - wrong = "thsoe" and right = "those" - or - wrong = "thta" and right = "that" - or - wrong = "thyat" and right = "that" - or - wrong = "tiem" and right = "tim" - or - wrong = "tiem" and right = "time" - or - wrong = "tihkn" and right = "think" - or - wrong = "tihs" and right = "this" - or - wrong = "timne" and right = "time" - or - wrong = "tiome" and right = "time" - or - wrong = "tiome" and right = "tome" - or - wrong = "tje" and right = "the" - or - wrong = "tjhe" and right = "the" - or - wrong = "tjpanishad" and right = "upanishad" - or - wrong = "tkae" and right = "take" - or - wrong = "tkaes" and right = "takes" - or - wrong = "tkaing" and right = "taking" - or - wrong = "tlaking" and right = "talking" - or - wrong = "tobbaco" and right = "tobacco" - or - wrong = "todya" and right = "today" - or - wrong = "toghether" and right = "together" - or - wrong = "toke" and right = "took" - or - wrong = "tolerence" and right = "tolerance" - or - wrong = "tolkein" and right = "tolkien" - or - wrong = "tomatos" and right = "tomatoes" - or - wrong = "tommorow" and right = "tomorrow" - or - wrong = "tommorrow" and right = "tomorrow" - or - wrong = "tongiht" and right = "tonight" - or - wrong = "toriodal" and right = "toroidal" - or - wrong = "tormenters" and right = "tormentors" - or - wrong = "tornadoe" and right = "tornado" - or - wrong = "torpeados" and right = "torpedoes" - or - wrong = "torpedos" and right = "torpedoes" - or - wrong = "tortise" and right = "tortoise" - or - wrong = "toubles" and right = "troubles" - or - wrong = "tounge" and right = "tongue" - or - wrong = "tourch" and right = "torch" - or - wrong = "tourch" and right = "touch" - or - wrong = "towords" and right = "towards" - or - wrong = "towrad" and right = "toward" - or - wrong = "tradionally" and right = "traditionally" - or - wrong = "traditionaly" and right = "traditionally" - or - wrong = "traditionnal" and right = "traditional" - or - wrong = "traditition" and right = "tradition" - or - wrong = "tradtionally" and right = "traditionally" - or - wrong = "trafficed" and right = "trafficked" - or - wrong = "trafficing" and right = "trafficking" - or - wrong = "trafic" and right = "traffic" - or - wrong = "trancendent" and right = "transcendent" - or - wrong = "trancending" and right = "transcending" - or - wrong = "tranform" and right = "transform" - or - wrong = "tranformed" and right = "transformed" - or - wrong = "transcendance" and right = "transcendence" - or - wrong = "transcendant" and right = "transcendent" - or - wrong = "transcendentational" and right = "transcendental" - or - wrong = "transcripting" and right = "transcribing" - or - wrong = "transcripting" and right = "transcription" - or - wrong = "transending" and right = "transcending" - or - wrong = "transesxuals" and right = "transsexuals" - or - wrong = "transfered" and right = "transferred" - or - wrong = "transfering" and right = "transferring" - or - wrong = "transformaton" and right = "transformation" - or - wrong = "transistion" and right = "transition" - or - wrong = "translater" and right = "translator" - or - wrong = "translaters" and right = "translators" - or - wrong = "transmissable" and right = "transmissible" - or - wrong = "transporation" and right = "transportation" - or - wrong = "tremelo" and right = "tremolo" - or - wrong = "tremelos" and right = "tremolos" - or - wrong = "treshold" and right = "threshold" - or - wrong = "triguered" and right = "triggered" - or - wrong = "triology" and right = "trilogy" - or - wrong = "troling" and right = "trolling" - or - wrong = "troup" and right = "troupe" - or - wrong = "troups" and right = "troops" - or - wrong = "troups" and right = "troupes" - or - wrong = "truely" and right = "truly" - or - wrong = "trustworthyness" and right = "trustworthiness" - or - wrong = "turnk" and right = "trunk" - or - wrong = "turnk" and right = "turnkey" - or - wrong = "tuscon" and right = "tucson" - or - wrong = "tust" and right = "trust" - or - wrong = "twelth" and right = "twelfth" - or - wrong = "twon" and right = "town" - or - wrong = "twpo" and right = "two" - or - wrong = "tyhat" and right = "that" - or - wrong = "tyhe" and right = "they" - or - wrong = "typcial" and right = "typical" - or - wrong = "typicaly" and right = "typically" - or - wrong = "tyranies" and right = "tyrannies" - or - wrong = "tyrany" and right = "tyranny" - or - wrong = "tyrranies" and right = "tyrannies" - or - wrong = "tyrrany" and right = "tyranny" - or - wrong = "ubiquitious" and right = "ubiquitous" - or - wrong = "ublisher" and right = "publisher" - or - wrong = "udpate" and right = "update" - or - wrong = "uise" and right = "use" - or - wrong = "ukranian" and right = "ukrainian" - or - wrong = "ultimely" and right = "ultimately" - or - wrong = "unacompanied" and right = "unaccompanied" - or - wrong = "unahppy" and right = "unhappy" - or - wrong = "unanymous" and right = "unanimous" - or - wrong = "unathorised" and right = "unauthorised" - or - wrong = "unavailible" and right = "unavailable" - or - wrong = "unballance" and right = "unbalance" - or - wrong = "unbeknowst" and right = "unbeknownst" - or - wrong = "unbeleivable" and right = "unbelievable" - or - wrong = "uncertainity" and right = "uncertainty" - or - wrong = "unchallengable" and right = "unchallengeable" - or - wrong = "unchangable" and right = "unchangeable" - or - wrong = "uncompetive" and right = "uncompetitive" - or - wrong = "unconcious" and right = "unconscious" - or - wrong = "unconciousness" and right = "unconsciousness" - or - wrong = "unconfortability" and right = "discomfort" - or - wrong = "uncontitutional" and right = "unconstitutional" - or - wrong = "unconvential" and right = "unconventional" - or - wrong = "undecideable" and right = "undecidable" - or - wrong = "understoon" and right = "understood" - or - wrong = "undesireable" and right = "undesirable" - or - wrong = "undetecable" and right = "undetectable" - or - wrong = "undoubtely" and right = "undoubtedly" - or - wrong = "undreground" and right = "underground" - or - wrong = "uneccesary" and right = "unnecessary" - or - wrong = "unecessary" and right = "unnecessary" - or - wrong = "unequalities" and right = "inequalities" - or - wrong = "unforetunately" and right = "unfortunately" - or - wrong = "unforgetable" and right = "unforgettable" - or - wrong = "unforgiveable" and right = "unforgivable" - or - wrong = "unforseen" and right = "unforeseen" - or - wrong = "unfortunatley" and right = "unfortunately" - or - wrong = "unfortunatly" and right = "unfortunately" - or - wrong = "unfourtunately" and right = "unfortunately" - or - wrong = "unihabited" and right = "uninhabited" - or - wrong = "unilateraly" and right = "unilaterally" - or - wrong = "unilatreal" and right = "unilateral" - or - wrong = "unilatreally" and right = "unilaterally" - or - wrong = "uninterruped" and right = "uninterrupted" - or - wrong = "uninterupted" and right = "uninterrupted" - or - wrong = "unintialized" and right = "uninitialized" - or - wrong = "unitesstates" and right = "unitedstates" - or - wrong = "univeral" and right = "universal" - or - wrong = "univeristies" and right = "universities" - or - wrong = "univeristy" and right = "university" - or - wrong = "univerity" and right = "university" - or - wrong = "universtiy" and right = "university" - or - wrong = "univesities" and right = "universities" - or - wrong = "univesity" and right = "university" - or - wrong = "unkown" and right = "unknown" - or - wrong = "unlikey" and right = "unlikely" - or - wrong = "unmanouverable" and right = "unmaneuverable" - or - wrong = "unmanouverable" and right = "unmanoeuvrable" - or - wrong = "unmistakeably" and right = "unmistakably" - or - wrong = "unneccesarily" and right = "unnecessarily" - or - wrong = "unneccesary" and right = "unnecessary" - or - wrong = "unneccessarily" and right = "unnecessarily" - or - wrong = "unneccessary" and right = "unnecessary" - or - wrong = "unnecesarily" and right = "unnecessarily" - or - wrong = "unnecesary" and right = "unnecessary" - or - wrong = "unoffical" and right = "unofficial" - or - wrong = "unoperational" and right = "nonoperational" - or - wrong = "unoticeable" and right = "unnoticeable" - or - wrong = "unplease" and right = "displease" - or - wrong = "unplesant" and right = "unpleasant" - or - wrong = "unprecendented" and right = "unprecedented" - or - wrong = "unprecidented" and right = "unprecedented" - or - wrong = "unrepentent" and right = "unrepentant" - or - wrong = "unrepetant" and right = "unrepentant" - or - wrong = "unrepetent" and right = "unrepentant" - or - wrong = "unsed" and right = "unsaid" - or - wrong = "unsed" and right = "unused" - or - wrong = "unsed" and right = "used" - or - wrong = "unsubstanciated" and right = "unsubstantiated" - or - wrong = "unsuccesful" and right = "unsuccessful" - or - wrong = "unsuccesfully" and right = "unsuccessfully" - or - wrong = "unsuccessfull" and right = "unsuccessful" - or - wrong = "unsucesful" and right = "unsuccessful" - or - wrong = "unsucesfuly" and right = "unsuccessfully" - or - wrong = "unsucessful" and right = "unsuccessful" - or - wrong = "unsucessfull" and right = "unsuccessful" - or - wrong = "unsucessfully" and right = "unsuccessfully" - or - wrong = "unsuprised" and right = "unsurprised" - or - wrong = "unsuprising" and right = "unsurprising" - or - wrong = "unsuprisingly" and right = "unsurprisingly" - or - wrong = "unsuprized" and right = "unsurprised" - or - wrong = "unsuprizing" and right = "unsurprising" - or - wrong = "unsuprizingly" and right = "unsurprisingly" - or - wrong = "unsurprized" and right = "unsurprised" - or - wrong = "unsurprizing" and right = "unsurprising" - or - wrong = "unsurprizingly" and right = "unsurprisingly" - or - wrong = "untill" and right = "until" - or - wrong = "untranslateable" and right = "untranslatable" - or - wrong = "unuseable" and right = "unusable" - or - wrong = "unusuable" and right = "unusable" - or - wrong = "unviersity" and right = "university" - or - wrong = "unwarrented" and right = "unwarranted" - or - wrong = "unweildly" and right = "unwieldy" - or - wrong = "unwieldly" and right = "unwieldy" - or - wrong = "upcomming" and right = "upcoming" - or - wrong = "upgradded" and right = "upgraded" - or - wrong = "usally" and right = "usually" - or - wrong = "useage" and right = "usage" - or - wrong = "usefull" and right = "useful" - or - wrong = "usefuly" and right = "usefully" - or - wrong = "useing" and right = "using" - or - wrong = "usualy" and right = "usually" - or - wrong = "ususally" and right = "usually" - or - wrong = "vaccum" and right = "vacuum" - or - wrong = "vaccume" and right = "vacuum" - or - wrong = "vacinity" and right = "vicinity" - or - wrong = "vaguaries" and right = "vagaries" - or - wrong = "vaieties" and right = "varieties" - or - wrong = "vailidty" and right = "validity" - or - wrong = "valetta" and right = "valletta" - or - wrong = "valuble" and right = "valuable" - or - wrong = "valueable" and right = "valuable" - or - wrong = "varations" and right = "variations" - or - wrong = "varient" and right = "variant" - or - wrong = "variey" and right = "variety" - or - wrong = "varing" and right = "varying" - or - wrong = "varities" and right = "varieties" - or - wrong = "varity" and right = "variety" - or - wrong = "vasall" and right = "vassal" - or - wrong = "vasalls" and right = "vassals" - or - wrong = "vaule" and right = "value" - or - wrong = "vegatarian" and right = "vegetarian" - or - wrong = "vegitable" and right = "vegetable" - or - wrong = "vegitables" and right = "vegetables" - or - wrong = "vegtable" and right = "vegetable" - or - wrong = "vehicule" and right = "vehicle" - or - wrong = "vell" and right = "well" - or - wrong = "venemous" and right = "venomous" - or - wrong = "vengance" and right = "vengeance" - or - wrong = "vengence" and right = "vengeance" - or - wrong = "verfication" and right = "verification" - or - wrong = "verison" and right = "version" - or - wrong = "verisons" and right = "versions" - or - wrong = "vermillion" and right = "vermilion" - or - wrong = "versitilaty" and right = "versatility" - or - wrong = "versitlity" and right = "versatility" - or - wrong = "vetween" and right = "between" - or - wrong = "veyr" and right = "very" - or - wrong = "vigeur" and right = "vigor" - or - wrong = "vigeur" and right = "vigour" - or - wrong = "vigeur" and right = "vigueur" - or - wrong = "vigilence" and right = "vigilance" - or - wrong = "vigourous" and right = "vigorous" - or - wrong = "villian" and right = "villain" - or - wrong = "villification" and right = "vilification" - or - wrong = "villify" and right = "vilify" - or - wrong = "villin" and right = "villain" - or - wrong = "villin" and right = "villein" - or - wrong = "villin" and right = "villi" - or - wrong = "vincinity" and right = "vicinity" - or - wrong = "violentce" and right = "violence" - or - wrong = "virtualy" and right = "virtually" - or - wrong = "virutal" and right = "virtual" - or - wrong = "virutally" and right = "virtually" - or - wrong = "visable" and right = "visible" - or - wrong = "visably" and right = "visibly" - or - wrong = "visting" and right = "visiting" - or - wrong = "vistors" and right = "visitors" - or - wrong = "vitories" and right = "victories" - or - wrong = "volcanoe" and right = "volcano" - or - wrong = "voleyball" and right = "volleyball" - or - wrong = "volontary" and right = "voluntary" - or - wrong = "volonteer" and right = "volunteer" - or - wrong = "volonteered" and right = "volunteered" - or - wrong = "volonteering" and right = "volunteering" - or - wrong = "volonteers" and right = "volunteers" - or - wrong = "volounteer" and right = "volunteer" - or - wrong = "volounteered" and right = "volunteered" - or - wrong = "volounteering" and right = "volunteering" - or - wrong = "volounteers" and right = "volunteers" - or - wrong = "volumne" and right = "volume" - or - wrong = "vreity" and right = "variety" - or - wrong = "vrey" and right = "very" - or - wrong = "vriety" and right = "variety" - or - wrong = "vulnerablility" and right = "vulnerability" - or - wrong = "vyer" and right = "very" - or - wrong = "vyre" and right = "very" - or - wrong = "waht" and right = "what" - or - wrong = "warantee" and right = "warranty" - or - wrong = "wardobe" and right = "wardrobe" - or - wrong = "warrent" and right = "warrant" - or - wrong = "warrriors" and right = "warriors" - or - wrong = "wass" and right = "was" - or - wrong = "watn" and right = "want" - or - wrong = "wayword" and right = "wayward" - or - wrong = "weaponary" and right = "weaponry" - or - wrong = "weas" and right = "was" - or - wrong = "wehn" and right = "when" - or - wrong = "weild" and right = "wield" - or - wrong = "weild" and right = "wild" - or - wrong = "weilded" and right = "wielded" - or - wrong = "wendsay" and right = "wednesday" - or - wrong = "wensday" and right = "wednesday" - or - wrong = "wereabouts" and right = "whereabouts" - or - wrong = "whant" and right = "want" - or - wrong = "whants" and right = "wants" - or - wrong = "whcih" and right = "which" - or - wrong = "wheras" and right = "whereas" - or - wrong = "wherease" and right = "whereas" - or - wrong = "whereever" and right = "wherever" - or - wrong = "whic" and right = "which" - or - wrong = "whihc" and right = "which" - or - wrong = "whith" and right = "with" - or - wrong = "whlch" and right = "which" - or - wrong = "whn" and right = "when" - or - wrong = "wholey" and right = "wholly" - or - wrong = "wholy" and right = "holy" - or - wrong = "wholy" and right = "wholly" - or - wrong = "whta" and right = "what" - or - wrong = "whther" and right = "whether" - or - wrong = "wich" and right = "which" - or - wrong = "wich" and right = "witch" - or - wrong = "widesread" and right = "widespread" - or - wrong = "wief" and right = "wife" - or - wrong = "wierd" and right = "weird" - or - wrong = "wiew" and right = "view" - or - wrong = "wih" and right = "with" - or - wrong = "wiht" and right = "with" - or - wrong = "wille" and right = "will" - or - wrong = "willingless" and right = "willingness" - or - wrong = "willk" and right = "will" - or - wrong = "wirting" and right = "writing" - or - wrong = "withdrawl" and right = "withdraw" - or - wrong = "withdrawl" and right = "withdrawal" - or - wrong = "witheld" and right = "withheld" - or - wrong = "withh" and right = "with" - or - wrong = "withing" and right = "within" - or - wrong = "withold" and right = "withhold" - or - wrong = "witht" and right = "with" - or - wrong = "witn" and right = "with" - or - wrong = "wiull" and right = "will" - or - wrong = "wnat" and right = "want" - or - wrong = "wnated" and right = "wanted" - or - wrong = "wnats" and right = "wants" - or - wrong = "wohle" and right = "whole" - or - wrong = "wokr" and right = "work" - or - wrong = "wokring" and right = "working" - or - wrong = "wonderfull" and right = "wonderful" - or - wrong = "wordlwide" and right = "worldwide" - or - wrong = "workststion" and right = "workstation" - or - wrong = "worls" and right = "world" - or - wrong = "worstened" and right = "worsened" - or - wrong = "woudl" and right = "would" - or - wrong = "wresters" and right = "wrestlers" - or - wrong = "wriet" and right = "write" - or - wrong = "writen" and right = "written" - or - wrong = "wroet" and right = "wrote" - or - wrong = "wrok" and right = "work" - or - wrong = "wroking" and right = "working" - or - wrong = "wtih" and right = "with" - or - wrong = "wupport" and right = "support" - or - wrong = "xenophoby" and right = "xenophobia" - or - wrong = "yaching" and right = "yachting" - or - wrong = "yaer" and right = "year" - or - wrong = "yaerly" and right = "yearly" - or - wrong = "yaers" and right = "years" - or - wrong = "yatch" and right = "yacht" - or - wrong = "yearm" and right = "year" - or - wrong = "yeasr" and right = "years" - or - wrong = "yeild" and right = "yield" - or - wrong = "yeilding" and right = "yielding" - or - wrong = "yementite" and right = "yemeni" - or - wrong = "yementite" and right = "yemenite" - or - wrong = "yera" and right = "year" - or - wrong = "yeras" and right = "years" - or - wrong = "yersa" and right = "years" - or - wrong = "yotube" and right = "youtube" - or - wrong = "youseff" and right = "yousef" - or - wrong = "youself" and right = "yourself" - or - wrong = "yrea" and right = "year" - or - wrong = "ytou" and right = "you" - or - wrong = "yuo" and right = "you" - or - wrong = "zeebra" and right = "zebra" -} +import codeql.typos.TypoDatabase as DB + +/** DEPRECATED: Use the `codeql/typos` pack instead. */ +deprecated predicate typos = DB::typos/2; diff --git a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql index 06e0a8146ef..4ddbe4516cc 100644 --- a/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql +++ b/javascript/ql/src/RegExp/RegExpAlwaysMatches.ql @@ -54,6 +54,8 @@ predicate isUniversalRegExp(RegExpTerm term) { or child.(RegExpCharacterClass).isUniversalClass() ) + or + term.(RegExpSequence).getNumChild() = 0 } /** @@ -116,6 +118,6 @@ where call instanceof RegExpSearchCall and not term.getAChild*() instanceof RegExpDollar and message = - "This regular expression always the matches at index 0 when used $@, as it matches the empty substring." + "This regular expression always matches at index 0 when used $@, as it matches the empty substring." ) select term, message, call, "here" diff --git a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql index 6240615a6e7..508bdad18b2 100644 --- a/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql +++ b/javascript/ql/src/Security/CWE-352/MissingCsrfMiddleware.ql @@ -35,7 +35,7 @@ predicate isRouteHandlerUsingCookies(Routing::RouteHandler handler) { * A router handler following after cookie parsing is assumed to depend on * cookies, and thus require CSRF protection. */ -predicate hasCookieMiddleware(Routing::Node route, HTTP::CookieMiddlewareInstance cookie) { +predicate hasCookieMiddleware(Routing::Node route, Http::CookieMiddlewareInstance cookie) { route.isGuardedBy(cookie) } @@ -112,7 +112,7 @@ private DataFlow::SourceNode nodeLeadingToCsrfWriteOrCheck(DataFlow::TypeBackTra * Gets a route handler that sets an CSRF related cookie. */ private Routing::RouteHandler getAHandlerSettingCsrfCookie() { - exists(HTTP::CookieDefinition setCookie | + exists(Http::CookieDefinition setCookie | setCookie.getNameArgument().getStringValue().regexpMatch("(?i).*(csrf|xsrf).*") and result = Routing::getRouteHandler(setCookie.getRouteHandler()) ) @@ -180,7 +180,7 @@ predicate hasCsrfMiddleware(Routing::RouteHandler handler) { from Routing::RouteSetup setup, Routing::Node setupArg, Routing::RouteHandler handler, - HTTP::CookieMiddlewareInstance cookie + Http::CookieMiddlewareInstance cookie where // Require that the handler uses cookies and has cookie middleware. // diff --git a/javascript/ql/src/Security/CWE-451/MissingXFrameOptions.ql b/javascript/ql/src/Security/CWE-451/MissingXFrameOptions.ql index 316af8d4a36..6228d1539ea 100644 --- a/javascript/ql/src/Security/CWE-451/MissingXFrameOptions.ql +++ b/javascript/ql/src/Security/CWE-451/MissingXFrameOptions.ql @@ -15,6 +15,6 @@ import javascript import semmle.javascript.frameworks.HTTP -from HTTP::ServerDefinition server +from Http::ServerDefinition server where not exists(server.getARouteHandler().getAResponseHeader("x-frame-options")) select server, "This server never sets the 'X-Frame-Options' HTTP header." diff --git a/javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql b/javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql index 89aeb49b6f4..1dade2a88ca 100644 --- a/javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql +++ b/javascript/ql/src/Security/CWE-598/SensitiveGetQuery.ql @@ -14,7 +14,7 @@ import javascript from - Routing::RouteSetup setup, Routing::RouteHandler handler, HTTP::RequestInputAccess input, + Routing::RouteSetup setup, Routing::RouteHandler handler, Http::RequestInputAccess input, SensitiveNode sensitive where setup.getOwnHttpMethod() = "GET" and diff --git a/javascript/ql/src/Security/CWE-730/ServerCrash.ql b/javascript/ql/src/Security/CWE-730/ServerCrash.ql index f5de58d4516..c3cd7dcc842 100644 --- a/javascript/ql/src/Security/CWE-730/ServerCrash.ql +++ b/javascript/ql/src/Security/CWE-730/ServerCrash.ql @@ -88,7 +88,7 @@ Function reachableFromAsyncCallback() { * The main predicate of this query: used for both result display and path computation. */ predicate main( - HTTP::RouteHandler rh, AsyncSentinelCall async, AsyncCallback cb, LikelyExceptionThrower thrower + Http::RouteHandler rh, AsyncSentinelCall async, AsyncCallback cb, LikelyExceptionThrower thrower ) { async.getAsyncCallee() = cb and rh.getAstNode() = invokesCallbackThatThrowsUncaughtException(async, thrower) @@ -180,7 +180,7 @@ query predicate nodes(AstNode node) { } from - HTTP::RouteHandler rh, AsyncSentinelCall async, DataFlow::Node callbackArg, AsyncCallback cb, + Http::RouteHandler rh, AsyncSentinelCall async, DataFlow::Node callbackArg, AsyncCallback cb, ExprOrStmt crasher where main(rh, async, cb, crasher) and diff --git a/javascript/ql/src/change-notes/2022-09-13-regexp-always-matches-fp.md b/javascript/ql/src/change-notes/2022-09-13-regexp-always-matches-fp.md new file mode 100644 index 00000000000..9e3dc2e980d --- /dev/null +++ b/javascript/ql/src/change-notes/2022-09-13-regexp-always-matches-fp.md @@ -0,0 +1,6 @@ +--- +category: minorAnalysis +--- + +- The `js/regexp/always-matches` query will no longer report an empty regular expression as always + matching, as this is often the intended behavior. diff --git a/javascript/ql/src/experimental/poi/PoI.qll b/javascript/ql/src/experimental/poi/PoI.qll index 68143be6bdd..84134600406 100644 --- a/javascript/ql/src/experimental/poi/PoI.qll +++ b/javascript/ql/src/experimental/poi/PoI.qll @@ -77,7 +77,7 @@ private module StandardPoIs { UnpromotedRouteSetupPoI() { this = "UnpromotedRouteSetupPoI" } override predicate is(Node l0) { - l0 instanceof HTTP::RouteSetupCandidate and not l0 instanceof HTTP::RouteSetup + l0 instanceof Http::RouteSetupCandidate and not l0 instanceof Http::RouteSetup } } @@ -88,7 +88,7 @@ private module StandardPoIs { UnpromotedRouteHandlerPoI() { this = "UnpromotedRouteHandlerPoI" } override predicate is(Node l0) { - l0 instanceof HTTP::RouteHandlerCandidate and not l0 instanceof HTTP::RouteHandler + l0 instanceof Http::RouteHandlerCandidate and not l0 instanceof Http::RouteHandler } } @@ -98,7 +98,7 @@ private module StandardPoIs { class UnpromotedRouteHandlerWithFlowPoI extends PoI { UnpromotedRouteHandlerWithFlowPoI() { this = "UnpromotedRouteHandlerWithFlowPoI" } - private DataFlow::SourceNode track(HTTP::RouteHandlerCandidate cand, DataFlow::TypeTracker t) { + private DataFlow::SourceNode track(Http::RouteHandlerCandidate cand, DataFlow::TypeTracker t) { t.start() and result = cand or @@ -106,8 +106,8 @@ private module StandardPoIs { } override predicate is(Node l0, Node l1, string t1) { - l0 instanceof HTTP::RouteHandlerCandidate and - not l0 instanceof HTTP::RouteHandler and + l0 instanceof Http::RouteHandlerCandidate and + not l0 instanceof Http::RouteHandler and l1 = track(l0, TypeTracker::end()) and (if l1 = l0 then t1 = "ends here" else t1 = "starts/ends here") } diff --git a/javascript/ql/src/meta/analysis-quality/CandidateTracking.qll b/javascript/ql/src/meta/analysis-quality/CandidateTracking.qll index 509d5f2a482..72046169219 100644 --- a/javascript/ql/src/meta/analysis-quality/CandidateTracking.qll +++ b/javascript/ql/src/meta/analysis-quality/CandidateTracking.qll @@ -8,7 +8,7 @@ import javascript * Gets a source node to which `cand` may flow inter-procedurally, with `t` tracking * the state of flow. */ -DataFlow::SourceNode track(HTTP::RouteHandlerCandidate cand, DataFlow::TypeTracker t) { +DataFlow::SourceNode track(Http::RouteHandlerCandidate cand, DataFlow::TypeTracker t) { t.start() and result = cand or diff --git a/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql b/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql index 5da34036b18..fe09e0ea866 100644 --- a/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql +++ b/javascript/ql/src/meta/analysis-quality/RouteHandlers.ql @@ -11,6 +11,6 @@ import javascript import CallGraphQuality -HTTP::RouteHandler relevantRouteHandler() { not result.getFile() instanceof IgnoredFile } +Http::RouteHandler relevantRouteHandler() { not result.getFile() instanceof IgnoredFile } select projectRoot(), count(relevantRouteHandler()) diff --git a/javascript/ql/src/meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql b/javascript/ql/src/meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql index 89b26d734c5..c59d05932af 100644 --- a/javascript/ql/src/meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql +++ b/javascript/ql/src/meta/analysis-quality/UnpromotedRouteHandlerCandidate.ql @@ -11,10 +11,10 @@ import javascript import CandidateTracking -from HTTP::RouteHandlerCandidate rh +from Http::RouteHandlerCandidate rh where - not rh instanceof HTTP::RouteHandler and - not exists(HTTP::RouteSetupCandidate setup | + not rh instanceof Http::RouteHandler and + not exists(Http::RouteSetupCandidate setup | track(rh, DataFlow::TypeTracker::end()).flowsTo(setup.getARouteHandlerArg()) ) select rh, diff --git a/javascript/ql/src/meta/analysis-quality/UnpromotedRouteSetupCandidate.ql b/javascript/ql/src/meta/analysis-quality/UnpromotedRouteSetupCandidate.ql index 428f6b4e4b0..1fee417c396 100644 --- a/javascript/ql/src/meta/analysis-quality/UnpromotedRouteSetupCandidate.ql +++ b/javascript/ql/src/meta/analysis-quality/UnpromotedRouteSetupCandidate.ql @@ -11,10 +11,10 @@ import javascript import CandidateTracking -from HTTP::RouteSetupCandidate setup +from Http::RouteSetupCandidate setup where - not setup instanceof HTTP::RouteSetup and - exists(HTTP::RouteHandlerCandidate rh | + not setup instanceof Http::RouteSetup and + exists(Http::RouteHandlerCandidate rh | track(rh, DataFlow::TypeTracker::end()).flowsTo(setup.getARouteHandlerArg()) ) select setup, diff --git a/javascript/ql/src/meta/extraction-metrics/FileData.ql b/javascript/ql/src/meta/extraction-metrics/FileData.ql index 3d5cbfed59f..bb689cb99cf 100644 --- a/javascript/ql/src/meta/extraction-metrics/FileData.ql +++ b/javascript/ql/src/meta/extraction-metrics/FileData.ql @@ -16,10 +16,12 @@ FileWithExtractionMetrics getACacheHit(FileWithExtractionMetrics f) { result.isFromCache() } -from FileWithExtractionMetrics file, boolean fromCache -where (if file.isFromCache() then fromCache = true else fromCache = false) -select file.getAbsolutePath() as FILE, file.getCpuTime() as CPU_NANO, - file.getNumberOfLines() as LINES, count(Locatable n | n.getFile() = file) as LOCATABLES, - count(TypeAnnotation n | n.getFile() = file) as TYPES, file.getLength() as LENGTH, - fromCache as FROM_CACHE, count(getACacheMember(file.getCacheFile())) as CACHE_MEMBERS, - count(getACacheHit(file)) as CACHE_HITS, file.getCacheFile() as CACHE_FILE +from FileWithExtractionMetrics fileWithMetrics, boolean fromCache +where (if fileWithMetrics.isFromCache() then fromCache = true else fromCache = false) +select fileWithMetrics.getAbsolutePath() as file, fileWithMetrics.getCpuTime() as cpu_nano, + fileWithMetrics.getNumberOfLines() as lines, + count(Locatable n | n.getFile() = fileWithMetrics) as locatables, + count(TypeAnnotation n | n.getFile() = fileWithMetrics) as types, + fileWithMetrics.getLength() as length, fromCache as from_cache, + count(getACacheMember(fileWithMetrics.getCacheFile())) as cache_members, + count(getACacheHit(fileWithMetrics)) as cache_hits, fileWithMetrics.getCacheFile() as cache_file diff --git a/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql b/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql index 20e4bab4d35..c67e2caab50 100644 --- a/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql +++ b/javascript/ql/src/meta/extraction-metrics/PhaseTimings.ql @@ -9,5 +9,5 @@ import semmle.javascript.meta.ExtractionMetrics::ExtractionMetrics from PhaseName phaseName -select phaseName, Aggregated::getCpuTime(phaseName) as CPU_NANO, - Aggregated::getWallclockTime(phaseName) as WALLCLOCK_NANO +select phaseName, Aggregated::getCpuTime(phaseName) as cpu_nano, + Aggregated::getWallclockTime(phaseName) as wallclock_nano diff --git a/javascript/ql/src/qlpack.yml b/javascript/ql/src/qlpack.yml index b371302105e..6b057dd9c5e 100644 --- a/javascript/ql/src/qlpack.yml +++ b/javascript/ql/src/qlpack.yml @@ -9,3 +9,4 @@ defaultSuiteFile: codeql-suites/javascript-code-scanning.qls dependencies: codeql/javascript-all: "*" codeql/suite-helpers: "*" + codeql/typos: 0.0.1-dev diff --git a/javascript/ql/test/library-tests/frameworks/Express/CookieMiddlewareInstance.qll b/javascript/ql/test/library-tests/frameworks/Express/CookieMiddlewareInstance.qll index 2f69ec905ae..025aaf04d00 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/CookieMiddlewareInstance.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/CookieMiddlewareInstance.qll @@ -1,7 +1,7 @@ import javascript query predicate test_CookieMiddlewareInstance( - HTTP::CookieMiddlewareInstance instance, DataFlow::Node res + Http::CookieMiddlewareInstance instance, DataFlow::Node res ) { res = instance.getASecretKey() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/Express/HeaderAccess.qll index 22d1d6aee53..69d34318ae1 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/HeaderAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/HeaderAccess.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { +query predicate test_HeaderAccess(Http::RequestHeaderAccess access, string res) { res = access.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition.qll index 45ec0af926a..115aefb0e80 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Express::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Express::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_defines.qll index 2a82f238c29..eb8e7800cdd 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Express::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getAHeaderName.qll index f618f71ca34..dc534c8cfce 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Express::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getNameExpr.qll b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getNameExpr.qll index 2a7da38aee2..717d96cbba3 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getNameExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/HeaderDefinition_getNameExpr.qll @@ -1,7 +1,7 @@ import javascript query predicate test_HeaderDefinition_getNameExpr( - HTTP::ExplicitHeaderDefinition hd, DataFlow::Node res + Http::ExplicitHeaderDefinition hd, DataFlow::Node res ) { hd.getRouteHandler() instanceof Express::RouteHandler and res = hd.getNameNode() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RedirectInvocation.qll b/javascript/ql/test/library-tests/frameworks/Express/RedirectInvocation.qll index 909c3779416..3f1b8710454 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RedirectInvocation.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RedirectInvocation.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RedirectInvocation(HTTP::RedirectInvocation red, Express::RouteHandler rh) { +query predicate test_RedirectInvocation(Http::RedirectInvocation red, Express::RouteHandler rh) { rh = red.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RequestExpr.qll b/javascript/ql/test/library-tests/frameworks/Express/RequestExpr.qll index 87c0bf32b0f..07ff4f22e0a 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RequestExpr.qll @@ -1,6 +1,6 @@ import javascript -query predicate test_RequestExpr(Express::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(Express::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/Express/RequestInputAccess.qll index c27020e2aa1..8a7f2f046e2 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res0, Express::RouteHandler rh + Http::RequestInputAccess ria, string res0, Express::RouteHandler rh ) { ria.getRouteHandler() = rh and res0 = ria.getKind() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/ResponseBody.qll b/javascript/ql/test/library-tests/frameworks/Express/ResponseBody.qll index db571874490..8091681dfd7 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/ResponseBody.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/ResponseBody.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseBody(HTTP::ResponseBody rb, Express::RouteHandler rh) { +query predicate test_ResponseBody(Http::ResponseBody rb, Express::RouteHandler rh) { rb.getRouteHandler() = rh } diff --git a/javascript/ql/test/library-tests/frameworks/Express/ResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/Express/ResponseExpr.qll index ad96e943511..5e911c96993 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/ResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/ResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseExpr(Express::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(Express::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/ResponseSendArgument.qll b/javascript/ql/test/library-tests/frameworks/Express/ResponseSendArgument.qll index fec7829f284..5fe3699e89c 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/ResponseSendArgument.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/ResponseSendArgument.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseSendArgument(HTTP::ResponseSendArgument send, Express::RouteHandler rh) { +query predicate test_ResponseSendArgument(Http::ResponseSendArgument send, Express::RouteHandler rh) { rh = send.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouteHandlerContainer.qll b/javascript/ql/test/library-tests/frameworks/Express/RouteHandlerContainer.qll index af341dc3028..994e8a318e2 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouteHandlerContainer.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouteHandlerContainer.qll @@ -1,7 +1,7 @@ import javascript query predicate getRouteHandlerContainerStep( - HTTP::RouteHandlerCandidateContainer container, DataFlow::SourceNode handler, + Http::RouteHandlerCandidateContainer container, DataFlow::SourceNode handler, DataFlow::SourceNode access ) { handler = container.getRouteHandler(access) diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getARequestExpr.qll index 652b0a1bbde..04b657d87c3 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RouteHandler_getARequestExpr(Express::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Express::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseExpr.qll index 6f9fece338d..70fead62bd5 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RouteHandler_getAResponseExpr(Express::RouteHandler rh, HTTP::ResponseNode res) { +query predicate test_RouteHandler_getAResponseExpr(Express::RouteHandler rh, Http::ResponseNode res) { res = rh.getAResponseNode() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseHeader.qll index 24f59d9b604..490205a9881 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RouteHandler_getAResponseHeader( - Express::RouteHandler rh, string name, HTTP::HeaderDefinition res + Express::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouteSetup_getRequestMethod.qll b/javascript/ql/test/library-tests/frameworks/Express/RouteSetup_getRequestMethod.qll index 48b0477a710..22d02c637ea 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouteSetup_getRequestMethod.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouteSetup_getRequestMethod.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RouteSetup_getRequestMethod(Express::RouteSetup rs, HTTP::RequestMethodName res) { +query predicate test_RouteSetup_getRequestMethod(Express::RouteSetup rs, Http::RequestMethodName res) { res = rs.getRequestMethod() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/RouterDefinition_getARouteHandler.qll b/javascript/ql/test/library-tests/frameworks/Express/RouterDefinition_getARouteHandler.qll index d9a5314002b..0117cbc3e1e 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/RouterDefinition_getARouteHandler.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/RouterDefinition_getARouteHandler.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RouterDefinition_getARouteHandler( - Express::RouterDefinition r, HTTP::RouteHandler res + Express::RouterDefinition r, Http::RouteHandler res ) { res = r.getARouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Express/SetCookie.qll b/javascript/ql/test/library-tests/frameworks/Express/SetCookie.qll index adaa6cd3959..5e6c10d9ce4 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/SetCookie.qll +++ b/javascript/ql/test/library-tests/frameworks/Express/SetCookie.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_SetCookie(HTTP::CookieDefinition cookiedef, Express::RouteHandler rh) { +query predicate test_SetCookie(Http::CookieDefinition cookiedef, Express::RouteHandler rh) { rh = cookiedef.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Firebase/tests.ql b/javascript/ql/test/library-tests/frameworks/Firebase/tests.ql index 5cc891ec78c..13f4a097102 100644 --- a/javascript/ql/test/library-tests/frameworks/Firebase/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/Firebase/tests.ql @@ -6,8 +6,8 @@ query predicate firebaseSnapshot(DataFlow::SourceNode snap) { snap = Firebase::s query predicate firebaseVal(Firebase::FirebaseVal val) { any() } -query predicate requestInputAccess(HTTP::RequestInputAccess acc) { any() } +query predicate requestInputAccess(Http::RequestInputAccess acc) { any() } -query predicate responseSendArgument(HTTP::ResponseSendArgument send) { any() } +query predicate responseSendArgument(Http::ResponseSendArgument send) { any() } -query predicate routeHandler(HTTP::RouteHandler handler) { any() } +query predicate routeHandler(Http::RouteHandler handler) { any() } diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/AdditionalRouteHandlers.ql b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/AdditionalRouteHandlers.ql index 6f35b3854d3..d91d43b7cde 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/AdditionalRouteHandlers.ql +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/AdditionalRouteHandlers.ql @@ -1,5 +1,5 @@ import javascript private import semmle.javascript.heuristics.AdditionalRouteHandlers -from HTTP::RouteHandler rh +from Http::RouteHandler rh select rh diff --git a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/tests.ql b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/tests.ql index ce0066ac8df..c85948e7552 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/HTTP-heuristics/tests.ql @@ -1,9 +1,9 @@ import javascript -query predicate routeHandler(HTTP::RouteHandler rh) { any() } +query predicate routeHandler(Http::RouteHandler rh) { any() } -query predicate routeHandlerCandidate(HTTP::RouteHandlerCandidate rh) { any() } +query predicate routeHandlerCandidate(Http::RouteHandlerCandidate rh) { any() } -query predicate routeSetup(HTTP::RouteSetup rh) { any() } +query predicate routeSetup(Http::RouteSetup rh) { any() } -query predicate routeSetupCandidate(HTTP::RouteSetupCandidate rh) { any() } +query predicate routeSetupCandidate(Http::RouteSetupCandidate rh) { any() } diff --git a/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.ql b/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.ql index 8d8e4ab0362..03699197e14 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.ql +++ b/javascript/ql/test/library-tests/frameworks/HTTP/RemoteRequestInput.ql @@ -1,4 +1,4 @@ import javascript -from HTTP::RequestInputAccess input +from Http::RequestInputAccess input select input, input.getKind() diff --git a/javascript/ql/test/library-tests/frameworks/HTTP/ResponseBody.ql b/javascript/ql/test/library-tests/frameworks/HTTP/ResponseBody.ql index 7723b4e284b..ab7aa08ea08 100644 --- a/javascript/ql/test/library-tests/frameworks/HTTP/ResponseBody.ql +++ b/javascript/ql/test/library-tests/frameworks/HTTP/ResponseBody.ql @@ -1,4 +1,4 @@ import javascript -from HTTP::ResponseBody rb +from Http::ResponseBody rb select rb diff --git a/javascript/ql/test/library-tests/frameworks/Micro/TestMicro.ql b/javascript/ql/test/library-tests/frameworks/Micro/TestMicro.ql index 97f4d7caf1e..56bf865f138 100644 --- a/javascript/ql/test/library-tests/frameworks/Micro/TestMicro.ql +++ b/javascript/ql/test/library-tests/frameworks/Micro/TestMicro.ql @@ -1,17 +1,17 @@ import javascript -query HTTP::RouteHandler routeHandler() { any() } +query Http::RouteHandler routeHandler() { any() } -query HTTP::Servers::RequestSource requestSource() { any() } +query Http::Servers::RequestSource requestSource() { any() } -query HTTP::Servers::ResponseSource responseSource() { any() } +query Http::Servers::ResponseSource responseSource() { any() } -query HTTP::RequestInputAccess requestInputAccess(string kind) { kind = result.getKind() } +query Http::RequestInputAccess requestInputAccess(string kind) { kind = result.getKind() } -query HTTP::RequestInputAccess userControlledObject() { result.isUserControlledObject() } +query Http::RequestInputAccess userControlledObject() { result.isUserControlledObject() } -query HTTP::ResponseSendArgument responseSendArgument() { any() } +query Http::ResponseSendArgument responseSendArgument() { any() } -query HTTP::ResponseSendArgument responseSendArgumentHandler(HTTP::RouteHandler h) { +query Http::ResponseSendArgument responseSendArgumentHandler(Http::RouteHandler h) { h = result.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/Nest/test.ql b/javascript/ql/test/library-tests/frameworks/Nest/test.ql index 120727d2548..898d244bed2 100644 --- a/javascript/ql/test/library-tests/frameworks/Nest/test.ql +++ b/javascript/ql/test/library-tests/frameworks/Nest/test.ql @@ -1,19 +1,19 @@ import javascript private import semmle.javascript.security.dataflow.ServerSideUrlRedirectCustomizations -query HTTP::RouteHandler routeHandler() { any() } +query Http::RouteHandler routeHandler() { any() } -query HTTP::Servers::RequestSource requestSource() { any() } +query Http::Servers::RequestSource requestSource() { any() } -query HTTP::Servers::ResponseSource responseSource() { any() } +query Http::Servers::ResponseSource responseSource() { any() } query RemoteFlowSource requestInputAccess(string kind) { - kind = result.(HTTP::RequestInputAccess).getKind() + kind = result.(Http::RequestInputAccess).getKind() or - not result instanceof HTTP::RequestInputAccess and + not result instanceof Http::RequestInputAccess and kind = "RemoteFlowSource" } -query HTTP::ResponseSendArgument responseSendArgument() { any() } +query Http::ResponseSendArgument responseSendArgument() { any() } query ServerSideUrlRedirect::Sink redirectSink() { any() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderAccess.qll index 22d1d6aee53..69d34318ae1 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderAccess.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { +query predicate test_HeaderAccess(Http::RequestHeaderAccess access, string res) { res = access.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition.qll index c1255a117c0..ec753397e68 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, NodeJSLib::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, NodeJSLib::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_defines.qll index a07c98b46be..1bc5d691f70 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof NodeJSLib::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getAHeaderName.qll index 7fcda812700..69b5623bd08 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof NodeJSLib::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getNameExpr.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getNameExpr.qll index b62e44bf947..4e1c2c5364d 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getNameExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/HeaderDefinition_getNameExpr.qll @@ -1,7 +1,7 @@ import javascript query predicate test_HeaderDefinition_getNameExpr( - HTTP::ExplicitHeaderDefinition hd, DataFlow::Node res + Http::ExplicitHeaderDefinition hd, DataFlow::Node res ) { hd.getRouteHandler() instanceof NodeJSLib::RouteHandler and res = hd.getNameNode() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestExpr.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestExpr.qll index 282c04ce314..b9f4176406d 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RequestExpr(NodeJSLib::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(NodeJSLib::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestInputAccess.qll index 7134c96d028..2234da53435 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, NodeJSLib::RouteHandler rh + Http::RequestInputAccess ria, string res, NodeJSLib::RouteHandler rh ) { ria.getRouteHandler() = rh and res = ria.getKind() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseExpr.qll index b509b70c884..b8884efa167 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseExpr(NodeJSLib::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(NodeJSLib::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseSendArgument.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseSendArgument.qll index 7d859f4d569..890cc9f92fe 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseSendArgument.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ResponseSendArgument.qll @@ -1,7 +1,7 @@ import javascript query predicate test_ResponseSendArgument( - HTTP::ResponseSendArgument send, NodeJSLib::RouteHandler rh + Http::ResponseSendArgument send, NodeJSLib::RouteHandler rh ) { rh = send.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getARequestExpr.qll index 147e788d642..b1080b85811 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RouteHandler_getARequestExpr(NodeJSLib::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(NodeJSLib::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseExpr.qll index f2871754551..05d27f18222 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseExpr.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RouteHandler_getAResponseExpr( - NodeJSLib::RouteHandler rh, HTTP::ResponseNode res + NodeJSLib::RouteHandler rh, Http::ResponseNode res ) { res = rh.getAResponseNode() } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseHeader.qll index 6733b1bd917..1c14fc6ac11 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import semmle.javascript.frameworks.Express query predicate test_RouteHandler_getAResponseHeader( - NodeJSLib::RouteHandler rh, string name, HTTP::HeaderDefinition res + NodeJSLib::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ServerDefinition_getARouteHandler.qll b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ServerDefinition_getARouteHandler.qll index 1902689a53e..900b632faed 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/ServerDefinition_getARouteHandler.qll +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/ServerDefinition_getARouteHandler.qll @@ -1,7 +1,7 @@ import javascript query predicate test_ServerDefinition_getARouteHandler( - NodeJSLib::ServerDefinition s, HTTP::RouteHandler res + NodeJSLib::ServerDefinition s, Http::RouteHandler res ) { res = s.getARouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/connect/tests.ql b/javascript/ql/test/library-tests/frameworks/connect/tests.ql index 17211377fbf..6c11c8ffd6b 100644 --- a/javascript/ql/test/library-tests/frameworks/connect/tests.ql +++ b/javascript/ql/test/library-tests/frameworks/connect/tests.ql @@ -3,26 +3,26 @@ import javascript query predicate test_RouteSetup(Connect::RouteSetup rs) { any() } query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, Connect::RouteHandler rh + Http::RequestInputAccess ria, string res, Connect::RouteHandler rh ) { ria.getRouteHandler() = rh and res = ria.getKind() } query predicate test_RouteHandler_getAResponseHeader( - Connect::RouteHandler rh, string name, HTTP::HeaderDefinition res + Connect::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Connect::RouteHandler } -query predicate test_ResponseExpr(HTTP::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(Http::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Connect::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Connect::RouteHandler rh) { rh = hd.getRouteHandler() } @@ -30,13 +30,13 @@ query predicate test_RouteSetup_getServer(Connect::RouteSetup rs, DataFlow::Node res = rs.getServer() } -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Connect::RouteHandler and res = hd.getAHeaderName() } query predicate test_ServerDefinition(Connect::ServerDefinition s) { any() } -query predicate test_RouteHandler_getAResponseExpr(Connect::RouteHandler rh, HTTP::ResponseNode res) { +query predicate test_RouteHandler_getAResponseExpr(Connect::RouteHandler rh, Http::ResponseNode res) { res = rh.getAResponseNode() } @@ -48,7 +48,7 @@ query predicate test_RouteHandler(Connect::RouteHandler rh, DataFlow::Node res) res = rh.getServer() } -query predicate test_RequestExpr(HTTP::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(Http::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } @@ -56,6 +56,6 @@ query predicate test_Credentials(Connect::Credentials cr, string res) { res = cr.getCredentialsKind() } -query predicate test_RouteHandler_getARequestExpr(Connect::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Connect::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll index 22d1d6aee53..69d34318ae1 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderAccess.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { +query predicate test_HeaderAccess(Http::RequestHeaderAccess access, string res) { res = access.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll index bf3ef7fb7fc..f3cef14be34 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Fastify::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Fastify::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll index 43413a4dc4b..3bfab852a0c 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Fastify::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll index 62e1d62c06f..41ae7de8a97 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Fastify::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll b/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll index fffbf35a79e..e0044bcbee1 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/RedirectInvocation.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RedirectInvocation(HTTP::RedirectInvocation invk, Fastify::RouteHandler rh) { +query predicate test_RedirectInvocation(Http::RedirectInvocation invk, Fastify::RouteHandler rh) { invk.getRouteHandler() = rh } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll index d8cdc2a4ccb..bb649d75b4c 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, Fastify::RouteHandler rh, boolean isUserControlledObject + Http::RequestInputAccess ria, string res, Fastify::RouteHandler rh, boolean isUserControlledObject ) { ria.getRouteHandler() = rh and res = ria.getKind() and diff --git a/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll b/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll index 8f221b18bd7..de79ce0cd5b 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/ResponseSendArgument.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseSendArgument(HTTP::ResponseSendArgument arg, Fastify::RouteHandler rh) { +query predicate test_ResponseSendArgument(Http::ResponseSendArgument arg, Fastify::RouteHandler rh) { arg.getRouteHandler() = rh } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll index c709d84e745..b3e0fc1b098 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getARequestExpr(Fastify::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Fastify::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll index a5394519940..fd2f356be13 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/fastify/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import semmle.javascript.frameworks.Express query predicate test_RouteHandler_getAResponseHeader( - Fastify::RouteHandler rh, string name, HTTP::HeaderDefinition res + Fastify::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/hapi/HeaderAccess.qll index 22d1d6aee53..69d34318ae1 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/HeaderAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/HeaderAccess.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { +query predicate test_HeaderAccess(Http::RequestHeaderAccess access, string res) { res = access.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition.qll index 5ed82a5f591..612a1fa082b 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Hapi::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Hapi::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_defines.qll index 56ccb45dcfb..836b4e686f6 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Hapi::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_getAHeaderName.qll index 7bc108da973..06f3fe3f220 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Hapi::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/RequestExpr.qll b/javascript/ql/test/library-tests/frameworks/hapi/RequestExpr.qll index 6e3118f1345..166b20ce8ed 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/RequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/RequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RequestExpr(Hapi::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(Hapi::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/hapi/RequestInputAccess.qll index 4146cb91cb6..5ce1fe4b5f6 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, Hapi::RouteHandler rh + Http::RequestInputAccess ria, string res, Hapi::RouteHandler rh ) { ria.getRouteHandler() = rh and res = ria.getKind() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/ResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/hapi/ResponseExpr.qll index 93a356284fe..3e2c2bca5c6 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/ResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/ResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseExpr(Hapi::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(Hapi::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getARequestExpr.qll index 668ded20aea..06bbbe11fde 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getARequestExpr(Hapi::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Hapi::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getAResponseHeader.qll index 84aa08d92e1..4eb30875385 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/hapi/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import semmle.javascript.frameworks.Express query predicate test_RouteHandler_getAResponseHeader( - Hapi::RouteHandler rh, string name, HTTP::HeaderDefinition res + Hapi::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/library-tests/frameworks/koa/HeaderAccess.qll b/javascript/ql/test/library-tests/frameworks/koa/HeaderAccess.qll index 22d1d6aee53..69d34318ae1 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/HeaderAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/HeaderAccess.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderAccess(HTTP::RequestHeaderAccess access, string res) { +query predicate test_HeaderAccess(Http::RequestHeaderAccess access, string res) { res = access.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition.qll index 972b3999b41..338717d1d28 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Koa::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Koa::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_defines.qll index 1fd1f5550b8..d33943b9b57 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Koa::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_getAHeaderName.qll index faec0fac04d..20d6e34bb42 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Koa::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/RedirectInvocation.qll b/javascript/ql/test/library-tests/frameworks/koa/RedirectInvocation.qll index c6d46ef8a02..802fc6a5c18 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RedirectInvocation.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RedirectInvocation.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RedirectInvocation( - HTTP::RedirectInvocation redirect, DataFlow::Node url, HTTP::RouteHandler rh + Http::RedirectInvocation redirect, DataFlow::Node url, Http::RouteHandler rh ) { redirect.getUrlArgument() = url and redirect.getRouteHandler() = rh diff --git a/javascript/ql/test/library-tests/frameworks/koa/RequestExpr.qll b/javascript/ql/test/library-tests/frameworks/koa/RequestExpr.qll index bcb3177b053..94e18fa6477 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RequestExpr(Koa::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(Koa::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/koa/RequestInputAccess.qll index 2e4e226fd3e..2c1601520f1 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, Koa::RouteHandler rh + Http::RequestInputAccess ria, string res, Koa::RouteHandler rh ) { ria.getRouteHandler() = rh and res = ria.getKind() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/ResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/koa/ResponseExpr.qll index 908dcdc745f..a6d1108ebec 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/ResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/ResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseExpr(Koa::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(Koa::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/ResponseSendArgument.qll b/javascript/ql/test/library-tests/frameworks/koa/ResponseSendArgument.qll index f0c3e9d2ea2..ccec2620e4f 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/ResponseSendArgument.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/ResponseSendArgument.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseSendArgument(HTTP::ResponseSendArgument send, Koa::RouteHandler rh) { +query predicate test_ResponseSendArgument(Http::ResponseSendArgument send, Koa::RouteHandler rh) { rh = send.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getARequestExpr.qll index 3c844c953e4..7e251ad1ea8 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getARequestExpr(Koa::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Koa::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseExpr.qll index 492386d4053..045a8b5fae4 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getAResponseExpr(Koa::RouteHandler rh, HTTP::ResponseNode res) { +query predicate test_RouteHandler_getAResponseExpr(Koa::RouteHandler rh, Http::ResponseNode res) { res = rh.getAResponseNode() } diff --git a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseHeader.qll index 98c613dc5c8..f1b8b481d36 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/koa/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import semmle.javascript.frameworks.Express query predicate test_RouteHandler_getAResponseHeader( - Koa::RouteHandler rh, string name, HTTP::HeaderDefinition res + Koa::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition.qll b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition.qll index 9e101f7ea37..d5661ccaa94 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition(HTTP::HeaderDefinition hd, Restify::RouteHandler rh) { +query predicate test_HeaderDefinition(Http::HeaderDefinition hd, Restify::RouteHandler rh) { rh = hd.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_defines.qll b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_defines.qll index 3f6694173ca..8851bc55c77 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_defines.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_defines.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_defines(HTTP::HeaderDefinition hd, string name, string value) { +query predicate test_HeaderDefinition_defines(Http::HeaderDefinition hd, string name, string value) { hd.defines(name, value) and hd.getRouteHandler() instanceof Restify::RouteHandler } diff --git a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_getAHeaderName.qll b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_getAHeaderName.qll index f546a156991..9881464b56b 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_getAHeaderName.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/HeaderDefinition_getAHeaderName.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_HeaderDefinition_getAHeaderName(HTTP::HeaderDefinition hd, string res) { +query predicate test_HeaderDefinition_getAHeaderName(Http::HeaderDefinition hd, string res) { hd.getRouteHandler() instanceof Restify::RouteHandler and res = hd.getAHeaderName() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/RequestExpr.qll b/javascript/ql/test/library-tests/frameworks/restify/RequestExpr.qll index c4759c6892f..2c4f75d365a 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/RequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/RequestExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_RequestExpr(Restify::RequestNode e, HTTP::RouteHandler res) { +query predicate test_RequestExpr(Restify::RequestNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/RequestInputAccess.qll b/javascript/ql/test/library-tests/frameworks/restify/RequestInputAccess.qll index 0c9f028f443..64cb1d74293 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/RequestInputAccess.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/RequestInputAccess.qll @@ -1,7 +1,7 @@ import javascript query predicate test_RequestInputAccess( - HTTP::RequestInputAccess ria, string res, Restify::RouteHandler rh + Http::RequestInputAccess ria, string res, Restify::RouteHandler rh ) { ria.getRouteHandler() = rh and res = ria.getKind() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/ResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/restify/ResponseExpr.qll index 1384ad6c850..b4fd9368a1d 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/ResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/ResponseExpr.qll @@ -1,5 +1,5 @@ import javascript -query predicate test_ResponseExpr(Restify::ResponseNode e, HTTP::RouteHandler res) { +query predicate test_ResponseExpr(Restify::ResponseNode e, Http::RouteHandler res) { res = e.getRouteHandler() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getARequestExpr.qll b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getARequestExpr.qll index d02c6f82ad3..4f76b514b2f 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getARequestExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getARequestExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getARequestExpr(Restify::RouteHandler rh, HTTP::RequestNode res) { +query predicate test_RouteHandler_getARequestExpr(Restify::RouteHandler rh, Http::RequestNode res) { res = rh.getARequestNode() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseExpr.qll b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseExpr.qll index e09d139d651..22d5d612a41 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseExpr.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseExpr.qll @@ -1,5 +1,5 @@ import semmle.javascript.frameworks.Express -query predicate test_RouteHandler_getAResponseExpr(Restify::RouteHandler rh, HTTP::ResponseNode res) { +query predicate test_RouteHandler_getAResponseExpr(Restify::RouteHandler rh, Http::ResponseNode res) { res = rh.getAResponseNode() } diff --git a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseHeader.qll b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseHeader.qll index d807f4879ab..f072f0146e6 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseHeader.qll +++ b/javascript/ql/test/library-tests/frameworks/restify/RouteHandler_getAResponseHeader.qll @@ -1,7 +1,7 @@ import semmle.javascript.frameworks.Express query predicate test_RouteHandler_getAResponseHeader( - Restify::RouteHandler rh, string name, HTTP::HeaderDefinition res + Restify::RouteHandler rh, string name, Http::HeaderDefinition res ) { res = rh.getAResponseHeader(name) } diff --git a/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/RegExpAlwaysMatches.expected b/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/RegExpAlwaysMatches.expected index 20c613bdd91..5d3568e728c 100644 --- a/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/RegExpAlwaysMatches.expected +++ b/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/RegExpAlwaysMatches.expected @@ -3,6 +3,5 @@ | tst.js:22:11:22:34 | ^(?:https?:\|ftp:\|file:)? | This regular expression always matches when used in a test $@, as it can match an empty substring. | tst.js:22:10:22:43 | /^(?:ht ... test(x) | here | | tst.js:30:11:30:20 | (foo\|bar)? | This regular expression always matches when used in a test $@, as it can match an empty substring. | tst.js:30:10:30:29 | /(foo\|bar)?/.test(x) | here | | tst.js:34:21:34:26 | (baz)? | This regular expression always matches when used in a test $@, as it can match an empty substring. | tst.js:34:10:34:35 | /^foo\|b ... test(x) | here | -| tst.js:58:20:58:25 | [a-z]* | This regular expression always the matches at index 0 when used $@, as it matches the empty substring. | tst.js:58:10:58:27 | x.search(/[a-z]*/) | here | -| tst.js:70:20:70:26 | ^(foo)? | This regular expression always the matches at index 0 when used $@, as it matches the empty substring. | tst.js:70:10:70:28 | x.search(/^(foo)?/) | here | -| tst.js:86:22:86:21 | | This regular expression always matches when used in a test $@, as it can match an empty substring. | tst.js:86:10:86:31 | new Reg ... test(x) | here | +| tst.js:58:20:58:25 | [a-z]* | This regular expression always matches at index 0 when used $@, as it matches the empty substring. | tst.js:58:10:58:27 | x.search(/[a-z]*/) | here | +| tst.js:70:20:70:26 | ^(foo)? | This regular expression always matches at index 0 when used $@, as it matches the empty substring. | tst.js:70:10:70:28 | x.search(/^(foo)?/) | here | diff --git a/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/tst.js b/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/tst.js index c9f581a6f65..a266e2d86f6 100644 --- a/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/tst.js +++ b/javascript/ql/test/query-tests/RegExp/RegExpAlwaysMatches/tst.js @@ -83,10 +83,10 @@ function nonWordBoundary(x) { } function emptyRegex(x) { - return new RegExp("").test(x); // NOT OK + return new RegExp("").test(x); // OK } function parserTest(x) { /(\w\s*:\s*[^:}]+|#){|@import[^\n]+(?:url|,)/.test(x); // OK - /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/.text(x); // ok -} \ No newline at end of file + /^((?:a{0,2}|-)|\w\{\d,\d\})+X$/.text(x); // ok +} diff --git a/javascript/ql/test/query-tests/Security/CWE-020/SuspiciousRegexpRange/tst.js b/javascript/ql/test/query-tests/Security/CWE-020/SuspiciousRegexpRange/tst.js index 62d325539ff..a698856d379 100644 --- a/javascript/ql/test/query-tests/Security/CWE-020/SuspiciousRegexpRange/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-020/SuspiciousRegexpRange/tst.js @@ -25,3 +25,6 @@ var numberToLetter = /[7-F]/; // NOT OK var overlapsWithClass1 = /[0-9\d]/; // NOT OK var overlapsWithClass2 = /[\w,.-?:*+]/; // NOT OK + +var tst2 = /^([ァ-ヾ]|[ァ-ン゙゚])+$/; // OK +var tst3 = /[0-90-9]/; // OK \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected index 1781accbe82..dfab0bdcf2b 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/UnsafeHtmlConstruction.expected @@ -8,6 +8,14 @@ nodes | jquery-plugin.js:12:31:12:41 | options.foo | | jquery-plugin.js:14:31:14:35 | stuff | | jquery-plugin.js:14:31:14:35 | stuff | +| lib2/index.ts:1:28:1:28 | s | +| lib2/index.ts:1:28:1:28 | s | +| lib2/index.ts:2:29:2:29 | s | +| lib2/index.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | +| lib/src/MyNode.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:2:29:2:29 | s | | main.js:1:55:1:55 | s | | main.js:1:55:1:55 | s | | main.js:2:29:2:29 | s | @@ -96,6 +104,14 @@ edges | jquery-plugin.js:11:34:11:40 | options | jquery-plugin.js:12:31:12:37 | options | | jquery-plugin.js:12:31:12:37 | options | jquery-plugin.js:12:31:12:41 | options.foo | | jquery-plugin.js:12:31:12:37 | options | jquery-plugin.js:12:31:12:41 | options.foo | +| lib2/index.ts:1:28:1:28 | s | lib2/index.ts:2:29:2:29 | s | +| lib2/index.ts:1:28:1:28 | s | lib2/index.ts:2:29:2:29 | s | +| lib2/index.ts:1:28:1:28 | s | lib2/index.ts:2:29:2:29 | s | +| lib2/index.ts:1:28:1:28 | s | lib2/index.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | lib/src/MyNode.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | lib/src/MyNode.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | lib/src/MyNode.ts:2:29:2:29 | s | +| lib/src/MyNode.ts:1:28:1:28 | s | lib/src/MyNode.ts:2:29:2:29 | s | | main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | | main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | | main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | @@ -183,6 +199,8 @@ edges #select | jquery-plugin.js:12:31:12:41 | options.foo | jquery-plugin.js:11:34:11:40 | options | jquery-plugin.js:12:31:12:41 | options.foo | $@ based on $@ might later cause $@. | jquery-plugin.js:12:31:12:41 | options.foo | HTML construction | jquery-plugin.js:11:34:11:40 | options | library input | jquery-plugin.js:12:20:12:53 | " ... /span>" | cross-site scripting | | jquery-plugin.js:14:31:14:35 | stuff | jquery-plugin.js:11:27:11:31 | stuff | jquery-plugin.js:14:31:14:35 | stuff | $@ based on $@ might later cause $@. | jquery-plugin.js:14:31:14:35 | stuff | HTML construction | jquery-plugin.js:11:27:11:31 | stuff | library input | jquery-plugin.js:14:20:14:47 | " ... /span>" | cross-site scripting | +| lib2/index.ts:2:29:2:29 | s | lib2/index.ts:1:28:1:28 | s | lib2/index.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | lib2/index.ts:2:29:2:29 | s | HTML construction | lib2/index.ts:1:28:1:28 | s | library input | lib2/index.ts:3:49:3:52 | html | cross-site scripting | +| lib/src/MyNode.ts:2:29:2:29 | s | lib/src/MyNode.ts:1:28:1:28 | s | lib/src/MyNode.ts:2:29:2:29 | s | $@ based on $@ might later cause $@. | lib/src/MyNode.ts:2:29:2:29 | s | HTML construction | lib/src/MyNode.ts:1:28:1:28 | s | library input | lib/src/MyNode.ts:3:49:3:52 | html | cross-site scripting | | main.js:2:29:2:29 | s | main.js:1:55:1:55 | s | main.js:2:29:2:29 | s | $@ based on $@ might later cause $@. | main.js:2:29:2:29 | s | HTML construction | main.js:1:55:1:55 | s | library input | main.js:3:49:3:52 | html | cross-site scripting | | main.js:7:49:7:49 | s | main.js:6:49:6:49 | s | main.js:7:49:7:49 | s | $@ based on $@ might later cause $@. | main.js:7:49:7:49 | s | XML parsing | main.js:6:49:6:49 | s | library input | main.js:8:48:8:66 | doc.documentElement | cross-site scripting | | main.js:12:49:12:49 | s | main.js:11:60:11:60 | s | main.js:12:49:12:49 | s | $@ based on $@ might later cause $@. | main.js:12:49:12:49 | s | XML parsing | main.js:11:60:11:60 | s | library input | main.js:16:21:16:35 | xml.cloneNode() | cross-site scripting | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/package.json b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/package.json new file mode 100644 index 00000000000..b849ee492e2 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/package.json @@ -0,0 +1,10 @@ +{ + "name": "my-unsafe-library", + "main": "./index.js", + "exports": { + "./MyNode": { + "require": "./lib/MyNode.cjs", + "import": "./lib/MyNode.mjs" + } + } +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/src/MyNode.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/src/MyNode.ts new file mode 100644 index 00000000000..91e81238605 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib/src/MyNode.ts @@ -0,0 +1,4 @@ +export function trivialXss(s: string) { + const html = "" + s + ""; // NOT OK + document.querySelector("#html").innerHTML = html; +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/index.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/index.ts new file mode 100644 index 00000000000..21bb420b71f --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/index.ts @@ -0,0 +1,4 @@ +export function trivialXss(s: string) { + const html = "" + s + ""; // NOT OK - this file is recognized as a main file. + document.querySelector("#html").innerHTML = html; +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/package.json b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/package.json new file mode 100644 index 00000000000..8c1cbff3c1d --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/package.json @@ -0,0 +1,10 @@ +{ + "name": "my-unsafe-library", + "main": "./foobar.js", + "exports": { + "./MyNode": { + "require": "./lib/MyNode.cjs", + "import": "./lib/MyNode.mjs" + } + } +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/src/MyNode.ts b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/src/MyNode.ts new file mode 100644 index 00000000000..35908c88f16 --- /dev/null +++ b/javascript/ql/test/query-tests/Security/CWE-079/UnsafeHtmlConstruction/lib2/src/MyNode.ts @@ -0,0 +1,4 @@ +export function trivialXss(s: string) { + const html = "" + s + ""; // OK - this file is not recognized as a main file. + document.querySelector("#html").innerHTML = html; +} \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected index 662b3315c80..7705d224298 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/PrototypePollutingAssignment.expected @@ -111,6 +111,12 @@ nodes | lib.js:119:13:119:24 | obj[path[0]] | | lib.js:119:17:119:20 | path | | lib.js:119:17:119:23 | path[0] | +| lib.js:127:14:127:17 | path | +| lib.js:127:14:127:17 | path | +| lib.js:128:9:128:20 | obj[path[0]] | +| lib.js:128:9:128:20 | obj[path[0]] | +| lib.js:128:13:128:16 | path | +| lib.js:128:13:128:19 | path[0] | | sublib/sub.js:1:37:1:40 | path | | sublib/sub.js:1:37:1:40 | path | | sublib/sub.js:2:3:2:14 | obj[path[0]] | @@ -165,6 +171,13 @@ nodes | tst.js:97:9:97:19 | req.query.x | | tst.js:97:9:97:19 | req.query.x | | tst.js:97:9:97:45 | req.que ... /g, '') | +| tst.js:102:9:102:38 | taint | +| tst.js:102:17:102:38 | String( ... y.data) | +| tst.js:102:24:102:37 | req.query.data | +| tst.js:102:24:102:37 | req.query.data | +| tst.js:105:5:105:17 | object[taint] | +| tst.js:105:5:105:17 | object[taint] | +| tst.js:105:12:105:16 | taint | edges | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | | lib.js:1:38:1:40 | obj | lib.js:6:7:6:9 | obj | @@ -269,6 +282,11 @@ edges | lib.js:119:17:119:20 | path | lib.js:119:17:119:23 | path[0] | | lib.js:119:17:119:23 | path[0] | lib.js:119:13:119:24 | obj[path[0]] | | lib.js:119:17:119:23 | path[0] | lib.js:119:13:119:24 | obj[path[0]] | +| lib.js:127:14:127:17 | path | lib.js:128:13:128:16 | path | +| lib.js:127:14:127:17 | path | lib.js:128:13:128:16 | path | +| lib.js:128:13:128:16 | path | lib.js:128:13:128:19 | path[0] | +| lib.js:128:13:128:19 | path[0] | lib.js:128:9:128:20 | obj[path[0]] | +| lib.js:128:13:128:19 | path[0] | lib.js:128:9:128:20 | obj[path[0]] | | sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:7:2:10 | path | | sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:7:2:10 | path | | sublib/sub.js:2:7:2:10 | path | sublib/sub.js:2:7:2:13 | path[0] | @@ -318,6 +336,12 @@ edges | tst.js:97:9:97:19 | req.query.x | tst.js:97:9:97:45 | req.que ... /g, '') | | tst.js:97:9:97:45 | req.que ... /g, '') | tst.js:97:5:97:46 | obj[req ... g, '')] | | tst.js:97:9:97:45 | req.que ... /g, '') | tst.js:97:5:97:46 | obj[req ... g, '')] | +| tst.js:102:9:102:38 | taint | tst.js:105:12:105:16 | taint | +| tst.js:102:17:102:38 | String( ... y.data) | tst.js:102:9:102:38 | taint | +| tst.js:102:24:102:37 | req.query.data | tst.js:102:17:102:38 | String( ... y.data) | +| tst.js:102:24:102:37 | req.query.data | tst.js:102:17:102:38 | String( ... y.data) | +| tst.js:105:12:105:16 | taint | tst.js:105:5:105:17 | object[taint] | +| tst.js:105:12:105:16 | taint | tst.js:105:5:105:17 | object[taint] | #select | lib.js:6:7:6:9 | obj | lib.js:1:43:1:46 | path | lib.js:6:7:6:9 | obj | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:1:43:1:46 | path | library input | | lib.js:15:3:15:14 | obj[path[0]] | lib.js:14:38:14:41 | path | lib.js:15:3:15:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:14:38:14:41 | path | library input | @@ -329,6 +353,7 @@ edges | lib.js:87:10:87:14 | proto | lib.js:83:14:83:22 | arguments | lib.js:87:10:87:14 | proto | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:83:14:83:22 | arguments | library input | | lib.js:108:3:108:10 | obj[one] | lib.js:104:13:104:21 | arguments | lib.js:108:3:108:10 | obj[one] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:104:13:104:21 | arguments | library input | | lib.js:119:13:119:24 | obj[path[0]] | lib.js:118:29:118:32 | path | lib.js:119:13:119:24 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:118:29:118:32 | path | library input | +| lib.js:128:9:128:20 | obj[path[0]] | lib.js:127:14:127:17 | path | lib.js:128:9:128:20 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:127:14:127:17 | path | library input | | sublib/sub.js:2:3:2:14 | obj[path[0]] | sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:3:2:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | sublib/sub.js:1:37:1:40 | path | library input | | tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input | | tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input | @@ -342,3 +367,4 @@ edges | tst.js:87:9:87:21 | object[taint] | tst.js:77:24:77:37 | req.query.data | tst.js:87:9:87:21 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:77:24:77:37 | req.query.data | user controlled input | | tst.js:94:5:94:37 | obj[req ... ', '')] | tst.js:94:9:94:19 | req.query.x | tst.js:94:5:94:37 | obj[req ... ', '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:94:9:94:19 | req.query.x | user controlled input | | tst.js:97:5:97:46 | obj[req ... g, '')] | tst.js:97:9:97:19 | req.query.x | tst.js:97:5:97:46 | obj[req ... g, '')] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:97:9:97:19 | req.query.x | user controlled input | +| tst.js:105:5:105:17 | object[taint] | tst.js:102:24:102:37 | req.query.data | tst.js:105:5:105:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:102:24:102:37 | req.query.data | user controlled input | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js index b65f6fb3c1f..fb550533d12 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/lib.js @@ -119,4 +119,19 @@ module.exports.returnsObj = function () { obj[path[0]][path[1]] = value; // NOT OK } } +} + +class MyClass { + constructor() {} + + set(obj, path, value) { + obj[path[0]][path[1]] = value; // NOT OK + } + + static staticSet(obj, path, value) { + obj[path[0]][path[1]] = value; // OK - not exported + } +} +module.exports.returnsMewMyClass = function () { + return new MyClass(); } \ No newline at end of file diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js index 61e2ff7300b..517d7d10230 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingAssignment/tst.js @@ -97,3 +97,17 @@ app.get('/foo', (req, res) => { obj[req.query.x.replace(/__proto__/g, '')].x = 'foo'; // NOT OK - "__pr__proto__oto__" obj[req.query.x.replace('o', '0')].x = 'foo'; // OK }); + +app.get('/bar', (req, res) => { + let taint = String(req.query.data); + + let object = {}; + object[taint][taint] = taint; // NOT OK + + const bad = ["__proto__", "constructor"]; + if (bad.includes(taint)) { + return; + } + + object[taint][taint] = taint; // OK +}); \ No newline at end of file diff --git a/python/ql/lib/change-notes/2022-09-12-CallNode-getArgByName.md b/python/ql/lib/change-notes/2022-09-12-CallNode-getArgByName.md new file mode 100644 index 00000000000..4bcde8386a8 --- /dev/null +++ b/python/ql/lib/change-notes/2022-09-12-CallNode-getArgByName.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Changed `CallNode.getArgByName` such that it has results for keyword arguments given after a dictionary unpacking argument, as the `bar=2` argument in `func(foo=1, **kwargs, bar=2)`. diff --git a/python/ql/lib/change-notes/2022-09-12-deprecate-pointsto.md b/python/ql/lib/change-notes/2022-09-12-deprecate-pointsto.md new file mode 100644 index 00000000000..e3b67321a40 --- /dev/null +++ b/python/ql/lib/change-notes/2022-09-12-deprecate-pointsto.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* Some unused predicates in `SsaDefinitions.qll`, `TObject.qll`, `protocols.qll`, and the `pointsto/` folder have been deprecated. \ No newline at end of file diff --git a/python/ql/lib/change-notes/2022-09-12-uppercase.md b/python/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/python/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/python/ql/lib/semmle/python/Concepts.qll b/python/ql/lib/semmle/python/Concepts.qll index b3d1cb6fe84..b4f1c73e633 100644 --- a/python/ql/lib/semmle/python/Concepts.qll +++ b/python/ql/lib/semmle/python/Concepts.qll @@ -565,7 +565,7 @@ module XML { } /** Provides classes for modeling LDAP-related APIs. */ -module LDAP { +module Ldap { /** * A data-flow node that executes an LDAP query. * @@ -598,6 +598,9 @@ module LDAP { } } +/** DEPRECATED: Alias for Ldap */ +deprecated module LDAP = Ldap; + /** * A data-flow node that escapes meta-characters, which could be used to prevent * injection attacks. @@ -706,7 +709,7 @@ class LdapFilterEscaping extends Escaping { } /** Provides classes for modeling HTTP-related APIs. */ -module HTTP { +module Http { /** Gets an HTTP verb, in upper case */ string httpVerb() { result in ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"] } @@ -917,7 +920,7 @@ module HTTP { * Extend this class to model new APIs. If you want to refine existing API models, * extend `HttpResponse` instead. */ - abstract class Range extends HTTP::Server::HttpResponse::Range { + abstract class Range extends Http::Server::HttpResponse::Range { /** Gets the data-flow node that specifies the location of this HTTP redirect response. */ abstract DataFlow::Node getRedirectLocation(); } @@ -1051,6 +1054,9 @@ module HTTP { // remote-flow-sources in general. } +/** DEPRECATED: Alias for Http */ +deprecated module HTTP = Http; + /** * Provides models for cryptographic things. * diff --git a/python/ql/lib/semmle/python/Flow.qll b/python/ql/lib/semmle/python/Flow.qll index ce886151135..b63a958efe6 100644 --- a/python/ql/lib/semmle/python/Flow.qll +++ b/python/ql/lib/semmle/python/Flow.qll @@ -376,7 +376,7 @@ class CallNode extends ControlFlowNode { ControlFlowNode getArgByName(string name) { exists(Call c, Keyword k | this.getNode() = c and - k = c.getAKeyword() and + k = c.getANamedArg() and k.getValue() = result.getNode() and k.getArg() = name and result.getBasicBlock().dominates(this.getBasicBlock()) diff --git a/python/ql/lib/semmle/python/essa/SsaDefinitions.qll b/python/ql/lib/semmle/python/essa/SsaDefinitions.qll index 810afc3179a..4896016eb78 100644 --- a/python/ql/lib/semmle/python/essa/SsaDefinitions.qll +++ b/python/ql/lib/semmle/python/essa/SsaDefinitions.qll @@ -78,7 +78,9 @@ module SsaSource { /** Holds if `v` is defined by a `for` statement, the definition being `defn` */ cached - predicate iteration_defined_variable(Variable v, ControlFlowNode defn, ControlFlowNode sequence) { + deprecated predicate iteration_defined_variable( + Variable v, ControlFlowNode defn, ControlFlowNode sequence + ) { exists(ForNode for | for.iterates(defn, sequence)) and defn.(NameNode).defines(v) } diff --git a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll index 5f687a01b49..2557b544ee1 100644 --- a/python/ql/lib/semmle/python/frameworks/Aiohttp.qll +++ b/python/ql/lib/semmle/python/frameworks/Aiohttp.qll @@ -59,7 +59,7 @@ module AiohttpWebModel { * Extend this class to refine existing API models. If you want to model new APIs, * extend `AiohttpRouteSetup::Range` instead. */ - class AiohttpRouteSetup extends HTTP::Server::RouteSetup::Range { + class AiohttpRouteSetup extends Http::Server::RouteSetup::Range { AiohttpRouteSetup::Range range; AiohttpRouteSetup() { this = range } @@ -161,7 +161,7 @@ module AiohttpWebModel { AiohttpAddRouteCall() { exists(string funcName | - funcName = HTTP::httpVerbLower() and + funcName = Http::httpVerbLower() and routeArgsStart = 0 or funcName = "view" and @@ -192,7 +192,7 @@ module AiohttpWebModel { AiohttpDecoratorRouteSetup() { exists(string decoratorName | - decoratorName = HTTP::httpVerbLower() and + decoratorName = Http::httpVerbLower() and routeArgsStart = 0 or decoratorName = "view" and @@ -237,7 +237,7 @@ module AiohttpWebModel { // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def result = this.getAMethod() and - result.getName() = HTTP::httpVerbLower() + result.getName() = Http::httpVerbLower() } } @@ -252,7 +252,7 @@ module AiohttpWebModel { } /** A request handler defined in an `aiohttp.web` view class, that has no known route. */ - private class AiohttpViewClassRequestHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range { + private class AiohttpViewClassRequestHandlerWithoutKnownRoute extends Http::Server::RequestHandler::Range { AiohttpViewClassRequestHandlerWithoutKnownRoute() { exists(AiohttpViewClass vc | vc.getARequestHandler() = this) and not exists(AiohttpRouteSetup setup | setup.getARequestHandler() = this) @@ -493,7 +493,7 @@ module AiohttpWebModel { * - https://docs.aiohttp.org/en/stable/web_reference.html#aiohttp.web.Response * - https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions */ - class AiohttpWebResponseInstantiation extends HTTP::Server::HttpResponse::Range, + class AiohttpWebResponseInstantiation extends Http::Server::HttpResponse::Range, Response::InstanceSource, DataFlow::CallCfgNode { API::Node apiNode; @@ -562,7 +562,7 @@ module AiohttpWebModel { * See the part about redirects at https://docs.aiohttp.org/en/stable/web_quickstart.html#aiohttp-web-exceptions */ class AiohttpRedirectExceptionInstantiation extends AiohttpWebResponseInstantiation, - HTTP::Server::HttpRedirectResponse::Range { + Http::Server::HttpRedirectResponse::Range { AiohttpRedirectExceptionInstantiation() { exists(string httpRedirectExceptionClassName | httpRedirectExceptionClassName in [ @@ -585,7 +585,7 @@ module AiohttpWebModel { /** * A call to `set_cookie` on a HTTP Response. */ - class AiohttpResponseSetCookieCall extends HTTP::Server::CookieWrite::Range, DataFlow::CallCfgNode { + class AiohttpResponseSetCookieCall extends Http::Server::CookieWrite::Range, DataFlow::CallCfgNode { AiohttpResponseSetCookieCall() { this = aiohttpResponseInstance().getMember("set_cookie").getACall() } @@ -600,7 +600,7 @@ module AiohttpWebModel { /** * A call to `del_cookie` on a HTTP Response. */ - class AiohttpResponseDelCookieCall extends HTTP::Server::CookieWrite::Range, DataFlow::CallCfgNode { + class AiohttpResponseDelCookieCall extends Http::Server::CookieWrite::Range, DataFlow::CallCfgNode { AiohttpResponseDelCookieCall() { this = aiohttpResponseInstance().getMember("del_cookie").getACall() } @@ -616,7 +616,7 @@ module AiohttpWebModel { * A dict-like write to an item of the `cookies` attribute on a HTTP response, such as * `response.cookies[name] = value`. */ - class AiohttpResponseCookieSubscriptWrite extends HTTP::Server::CookieWrite::Range { + class AiohttpResponseCookieSubscriptWrite extends Http::Server::CookieWrite::Range { DataFlow::Node index; DataFlow::Node value; @@ -661,11 +661,11 @@ private module AiohttpClientModel { private API::Node instance() { result = classRef().getReturn() } /** A method call on a ClientSession that sends off a request */ - private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode { + private class OutgoingRequestCall extends Http::Client::Request::Range, API::CallNode { string methodName; OutgoingRequestCall() { - methodName in [HTTP::httpVerbLower(), "request"] and + methodName in [Http::httpVerbLower(), "request"] and this = instance().getMember(methodName).getACall() } diff --git a/python/ql/lib/semmle/python/frameworks/Django.qll b/python/ql/lib/semmle/python/frameworks/Django.qll index a430513cb62..e67bb8cf385 100644 --- a/python/ql/lib/semmle/python/frameworks/Django.qll +++ b/python/ql/lib/semmle/python/frameworks/Django.qll @@ -1155,11 +1155,11 @@ module PrivateDjango { /** Gets a reference to the `django.http` module. */ API::Node http() { result = django().getMember("http") } - /** DEPRECATED: Alias for `Http` */ - deprecated module http = Http; + /** DEPRECATED: Alias for `DjangoHttp` */ + deprecated module http = DjangoHttp; /** Provides models for the `django.http` module */ - module Http { + module DjangoHttp { // --------------------------------------------------------------------------- // django.http.request // --------------------------------------------------------------------------- @@ -1251,7 +1251,7 @@ module PrivateDjango { // special handling of the `build_absolute_uri` method, see // https://docs.djangoproject.com/en/3.0/ref/request-response/#django.http.HttpRequest.build_absolute_uri exists(DataFlow::AttrRead attr, DataFlow::CallCfgNode call, DataFlow::Node instance | - instance = DjangoImpl::Http::Request::HttpRequest::instance() and + instance = DjangoImpl::DjangoHttp::Request::HttpRequest::instance() and attr.getObject() = instance | attr.getAttributeName() = "build_absolute_uri" and @@ -1359,7 +1359,7 @@ module PrivateDjango { * * Use the predicate `HttpResponse::instance()` to get references to instances of `django.http.response.HttpResponse`. */ - abstract class InstanceSource extends HTTP::Server::HttpResponse::Range, DataFlow::Node { + abstract class InstanceSource extends Http::Server::HttpResponse::Range, DataFlow::Node { } /** A direct instantiation of `django.http.response.HttpResponse`. */ @@ -1421,7 +1421,7 @@ module PrivateDjango { * Use the predicate `HttpResponseRedirect::instance()` to get references to instances of `django.http.response.HttpResponseRedirect`. */ abstract class InstanceSource extends HttpResponse::InstanceSource, - HTTP::Server::HttpRedirectResponse::Range, DataFlow::Node { } + Http::Server::HttpRedirectResponse::Range, DataFlow::Node { } /** A direct instantiation of `django.http.response.HttpResponseRedirect`. */ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode { @@ -1483,7 +1483,7 @@ module PrivateDjango { * Use the predicate `HttpResponsePermanentRedirect::instance()` to get references to instances of `django.http.response.HttpResponsePermanentRedirect`. */ abstract class InstanceSource extends HttpResponse::InstanceSource, - HTTP::Server::HttpRedirectResponse::Range, DataFlow::Node { } + Http::Server::HttpRedirectResponse::Range, DataFlow::Node { } /** A direct instantiation of `django.http.response.HttpResponsePermanentRedirect`. */ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode { @@ -2066,17 +2066,18 @@ module PrivateDjango { /** Gets a reference to the `django.http.response.HttpResponse.write` function. */ private DataFlow::TypeTrackingNode write( - DjangoImpl::Http::Response::HttpResponse::InstanceSource instance, DataFlow::TypeTracker t + DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance, + DataFlow::TypeTracker t ) { t.startInAttr("write") and - instance = DjangoImpl::Http::Response::HttpResponse::instance() and + instance = DjangoImpl::DjangoHttp::Response::HttpResponse::instance() and result = instance or exists(DataFlow::TypeTracker t2 | result = write(instance, t2).track(t2, t)) } /** Gets a reference to the `django.http.response.HttpResponse.write` function. */ - DataFlow::Node write(DjangoImpl::Http::Response::HttpResponse::InstanceSource instance) { + DataFlow::Node write(DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance) { write(instance, DataFlow::TypeTracker::end()).flowsTo(result) } @@ -2085,8 +2086,8 @@ module PrivateDjango { * * See https://docs.djangoproject.com/en/3.1/ref/request-response/#django.http.HttpResponse.write */ - class HttpResponseWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::CallCfgNode { - DjangoImpl::Http::Response::HttpResponse::InstanceSource instance; + class HttpResponseWriteCall extends Http::Server::HttpResponse::Range, DataFlow::CallCfgNode { + DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource instance; HttpResponseWriteCall() { this.getFunction() = write(instance) } @@ -2104,10 +2105,10 @@ module PrivateDjango { /** * A call to `set_cookie` on a HTTP Response. */ - class DjangoResponseSetCookieCall extends HTTP::Server::CookieWrite::Range, + class DjangoResponseSetCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { DjangoResponseSetCookieCall() { - this.calls(DjangoImpl::Http::Response::HttpResponse::instance(), "set_cookie") + this.calls(DjangoImpl::DjangoHttp::Response::HttpResponse::instance(), "set_cookie") } override DataFlow::Node getHeaderArg() { none() } @@ -2124,10 +2125,10 @@ module PrivateDjango { /** * A call to `delete_cookie` on a HTTP Response. */ - class DjangoResponseDeleteCookieCall extends HTTP::Server::CookieWrite::Range, + class DjangoResponseDeleteCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { DjangoResponseDeleteCookieCall() { - this.calls(DjangoImpl::Http::Response::HttpResponse::instance(), "delete_cookie") + this.calls(DjangoImpl::DjangoHttp::Response::HttpResponse::instance(), "delete_cookie") } override DataFlow::Node getHeaderArg() { none() } @@ -2143,7 +2144,7 @@ module PrivateDjango { * A dict-like write to an item of the `cookies` attribute on a HTTP response, such as * `response.cookies[name] = value`. */ - class DjangoResponseCookieSubscriptWrite extends HTTP::Server::CookieWrite::Range { + class DjangoResponseCookieSubscriptWrite extends Http::Server::CookieWrite::Range { DataFlow::Node index; DataFlow::Node value; @@ -2154,7 +2155,7 @@ module PrivateDjango { this.asCfgNode() = subscript | cookieLookup.getAttributeName() = "cookies" and - cookieLookup.getObject() = DjangoImpl::Http::Response::HttpResponse::instance() and + cookieLookup.getObject() = DjangoImpl::DjangoHttp::Response::HttpResponse::instance() and exists(DataFlow::Node subscriptObj | subscriptObj.asCfgNode() = subscript.getObject() | @@ -2315,7 +2316,7 @@ module PrivateDjango { // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def result = this.getAMethod() and ( - result.getName() = HTTP::httpVerbLower() + result.getName() = Http::httpVerbLower() or result.getName() = "get_redirect_url" ) @@ -2410,7 +2411,7 @@ module PrivateDjango { } /** A data-flow node that sets up a route on a server, using the django framework. */ - abstract class DjangoRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CfgNode { + abstract class DjangoRouteSetup extends Http::Server::RouteSetup::Range, DataFlow::CfgNode { /** Gets the data-flow node that is used as the argument for the view handler. */ abstract DataFlow::Node getViewArg(); @@ -2427,7 +2428,7 @@ module PrivateDjango { } /** A request handler defined in a django view class, that has no known route. */ - private class DjangoViewClassHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range, + private class DjangoViewClassHandlerWithoutKnownRoute extends Http::Server::RequestHandler::Range, DjangoRouteHandler { DjangoViewClassHandlerWithoutKnownRoute() { exists(DjangoViewClass vc | vc.getARequestHandler() = this) and @@ -2586,7 +2587,7 @@ module PrivateDjango { // HttpRequest taint modeling // --------------------------------------------------------------------------- /** A parameter that will receive the django `HttpRequest` instance when a request handler is invoked. */ - private class DjangoRequestHandlerRequestParam extends DjangoImpl::Http::Request::HttpRequest::InstanceSource, + private class DjangoRequestHandlerRequestParam extends DjangoImpl::DjangoHttp::Request::HttpRequest::InstanceSource, RemoteFlowSource::Range, DataFlow::ParameterNode { DjangoRequestHandlerRequestParam() { this.getParameter() = any(DjangoRouteSetup setup).getARequestHandler().getRequestParam() @@ -2603,7 +2604,7 @@ module PrivateDjango { * * See https://docs.djangoproject.com/en/3.1/topics/class-based-views/generic-display/#dynamic-filtering */ - private class DjangoViewClassRequestAttributeRead extends DjangoImpl::Http::Request::HttpRequest::InstanceSource, + private class DjangoViewClassRequestAttributeRead extends DjangoImpl::DjangoHttp::Request::HttpRequest::InstanceSource, RemoteFlowSource::Range, DataFlow::Node { DjangoViewClassRequestAttributeRead() { exists(DataFlow::AttrRead read | this = read | @@ -2678,7 +2679,7 @@ module PrivateDjango { * * See https://docs.djangoproject.com/en/3.1/topics/http/shortcuts/#redirect */ - private class DjangoShortcutsRedirectCall extends HTTP::Server::HttpRedirectResponse::Range, + private class DjangoShortcutsRedirectCall extends Http::Server::HttpRedirectResponse::Range, DataFlow::CallCfgNode { DjangoShortcutsRedirectCall() { this = DjangoImpl::Shortcuts::redirect().getACall() } @@ -2712,7 +2713,7 @@ module PrivateDjango { * * See https://docs.djangoproject.com/en/3.1/ref/class-based-views/base/#redirectview */ - private class DjangoRedirectViewGetRedirectUrlReturn extends HTTP::Server::HttpRedirectResponse::Range, + private class DjangoRedirectViewGetRedirectUrlReturn extends Http::Server::HttpRedirectResponse::Range, DataFlow::CfgNode { DjangoRedirectViewGetRedirectUrlReturn() { node = any(GetRedirectUrlFunction f).getAReturnValueFlowNode() @@ -2751,7 +2752,7 @@ module PrivateDjango { /** * A custom middleware stack */ - private class DjangoSettingsMiddlewareStack extends HTTP::Server::CsrfProtectionSetting::Range { + private class DjangoSettingsMiddlewareStack extends Http::Server::CsrfProtectionSetting::Range { List list; DjangoSettingsMiddlewareStack() { @@ -2787,7 +2788,7 @@ module PrivateDjango { } } - private class DjangoCsrfDecorator extends HTTP::Server::CsrfLocalProtectionSetting::Range { + private class DjangoCsrfDecorator extends Http::Server::CsrfLocalProtectionSetting::Range { string decoratorName; Function function; diff --git a/python/ql/lib/semmle/python/frameworks/FastApi.qll b/python/ql/lib/semmle/python/frameworks/FastApi.qll index f042dafea59..b45ef81f88a 100644 --- a/python/ql/lib/semmle/python/frameworks/FastApi.qll +++ b/python/ql/lib/semmle/python/frameworks/FastApi.qll @@ -48,10 +48,10 @@ private module FastApi { * * See https://fastapi.tiangolo.com/tutorial/first-steps/#define-a-path-operation-decorator */ - private class FastApiRouteSetup extends HTTP::Server::RouteSetup::Range, DataFlow::CallCfgNode { + private class FastApiRouteSetup extends Http::Server::RouteSetup::Range, DataFlow::CallCfgNode { FastApiRouteSetup() { exists(string routeAddingMethod | - routeAddingMethod = HTTP::httpVerbLower() + routeAddingMethod = Http::httpVerbLower() or routeAddingMethod in ["api_route", "websocket"] | @@ -195,7 +195,7 @@ private module FastApi { abstract class InstanceSource extends DataFlow::LocalSourceNode { } /** A direct instantiation of a response class. */ - private class ResponseInstantiation extends InstanceSource, HTTP::Server::HttpResponse::Range, + private class ResponseInstantiation extends InstanceSource, Http::Server::HttpResponse::Range, DataFlow::CallCfgNode { API::Node baseApiNode; API::Node responseClass; @@ -223,7 +223,7 @@ private module FastApi { * A direct instantiation of a redirect response. */ private class RedirectResponseInstantiation extends ResponseInstantiation, - HTTP::Server::HttpRedirectResponse::Range { + Http::Server::HttpRedirectResponse::Range { RedirectResponseInstantiation() { baseApiNode = getModeledResponseClass("RedirectResponse") } override DataFlow::Node getRedirectLocation() { @@ -245,7 +245,7 @@ private module FastApi { /** * An implicit response from a return of FastAPI request handler. */ - private class FastApiRequestHandlerReturn extends HTTP::Server::HttpResponse::Range, + private class FastApiRequestHandlerReturn extends Http::Server::HttpResponse::Range, DataFlow::CfgNode { FastApiRouteSetup routeSetup; @@ -291,7 +291,7 @@ private module FastApi { * `response_class` set to a `RedirectResponse`. */ private class FastApiRequestHandlerRedirectReturn extends FastApiRequestHandlerReturn, - HTTP::Server::HttpRedirectResponse::Range { + Http::Server::HttpRedirectResponse::Range { FastApiRequestHandlerRedirectReturn() { exists(API::Node responseClass | responseClass.getAValueReachableFromSource() = routeSetup.getResponseClassArg() and @@ -332,7 +332,7 @@ private module FastApi { /** * A call to `set_cookie` on a FastAPI Response. */ - private class SetCookieCall extends HTTP::Server::CookieWrite::Range, DataFlow::MethodCallNode { + private class SetCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { SetCookieCall() { this.calls(instance(), "set_cookie") } override DataFlow::Node getHeaderArg() { none() } @@ -348,7 +348,7 @@ private module FastApi { * A call to `append` on a `headers` of a FastAPI Response, with the `Set-Cookie` * header-key. */ - private class HeadersAppendCookie extends HTTP::Server::CookieWrite::Range, + private class HeadersAppendCookie extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { HeadersAppendCookie() { exists(DataFlow::AttrRead headers, DataFlow::Node keyArg | diff --git a/python/ql/lib/semmle/python/frameworks/Flask.qll b/python/ql/lib/semmle/python/frameworks/Flask.qll index 8ee93fcec52..890e45a2ab5 100644 --- a/python/ql/lib/semmle/python/frameworks/Flask.qll +++ b/python/ql/lib/semmle/python/frameworks/Flask.qll @@ -116,7 +116,7 @@ module Flask { * * Use the predicate `Response::instance()` to get references to instances of `flask.Response`. */ - abstract class InstanceSource extends HTTP::Server::HttpResponse::Range, DataFlow::Node { } + abstract class InstanceSource extends Http::Server::HttpResponse::Range, DataFlow::Node { } /** A direct instantiation of `flask.Response`. */ private class ClassInstantiation extends InstanceSource, DataFlow::CallCfgNode { @@ -229,7 +229,7 @@ module Flask { // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def result = this.getAMethod() and - result.getName() = HTTP::httpVerbLower() + result.getName() = Http::httpVerbLower() } } @@ -241,7 +241,7 @@ module Flask { } /** A route setup made by flask (sharing handling of URL patterns). */ - abstract class FlaskRouteSetup extends HTTP::Server::RouteSetup::Range { + abstract class FlaskRouteSetup extends Http::Server::RouteSetup::Range { override Parameter getARoutedParameter() { // If we don't know the URL pattern, we simply mark all parameters as a routed // parameter. This should give us more RemoteFlowSources but could also lead to @@ -312,7 +312,7 @@ module Flask { } /** A request handler defined in a django view class, that has no known route. */ - private class FlaskViewClassHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range { + private class FlaskViewClassHandlerWithoutKnownRoute extends Http::Server::RequestHandler::Range { FlaskViewClassHandlerWithoutKnownRoute() { exists(FlaskViewClass vc | vc.getARequestHandler() = this) and not exists(FlaskRouteSetup setup | setup.getARequestHandler() = this) @@ -439,7 +439,7 @@ module Flask { // --------------------------------------------------------------------------- // Implicit response from returns of flask request handlers // --------------------------------------------------------------------------- - private class FlaskRouteHandlerReturn extends HTTP::Server::HttpResponse::Range, DataFlow::CfgNode { + private class FlaskRouteHandlerReturn extends Http::Server::HttpResponse::Range, DataFlow::CfgNode { FlaskRouteHandlerReturn() { exists(Function routeHandler | routeHandler = any(FlaskRouteSetup rs).getARequestHandler() and @@ -462,7 +462,7 @@ module Flask { * * See https://flask.palletsprojects.com/en/1.1.x/api/#flask.redirect */ - private class FlaskRedirectCall extends HTTP::Server::HttpRedirectResponse::Range, + private class FlaskRedirectCall extends Http::Server::HttpRedirectResponse::Range, DataFlow::CallCfgNode { FlaskRedirectCall() { this = API::moduleImport("flask").getMember("redirect").getACall() } @@ -490,7 +490,7 @@ module Flask { * * See https://flask.palletsprojects.com/en/2.0.x/api/#flask.Response.set_cookie */ - class FlaskResponseSetCookieCall extends HTTP::Server::CookieWrite::Range, + class FlaskResponseSetCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { FlaskResponseSetCookieCall() { this.calls(Flask::Response::instance(), "set_cookie") } @@ -506,7 +506,7 @@ module Flask { * * See https://flask.palletsprojects.com/en/2.0.x/api/#flask.Response.delete_cookie */ - class FlaskResponseDeleteCookieCall extends HTTP::Server::CookieWrite::Range, + class FlaskResponseDeleteCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { FlaskResponseDeleteCookieCall() { this.calls(Flask::Response::instance(), "delete_cookie") } diff --git a/python/ql/lib/semmle/python/frameworks/Httpx.qll b/python/ql/lib/semmle/python/frameworks/Httpx.qll index b746de6ee5e..ec6a446db2e 100644 --- a/python/ql/lib/semmle/python/frameworks/Httpx.qll +++ b/python/ql/lib/semmle/python/frameworks/Httpx.qll @@ -23,11 +23,11 @@ private module HttpxModel { * * See https://www.python-httpx.org/api/ */ - private class RequestCall extends HTTP::Client::Request::Range, API::CallNode { + private class RequestCall extends Http::Client::Request::Range, API::CallNode { string methodName; RequestCall() { - methodName in [HTTP::httpVerbLower(), "request", "stream"] and + methodName in [Http::httpVerbLower(), "request", "stream"] and this = API::moduleImport("httpx").getMember(methodName).getACall() } @@ -64,11 +64,11 @@ private module HttpxModel { } /** A method call on a Client that sends off a request */ - private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class OutgoingRequestCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { string methodName; OutgoingRequestCall() { - methodName in [HTTP::httpVerbLower(), "request", "stream"] and + methodName in [Http::httpVerbLower(), "request", "stream"] and this = classRef().getReturn().getMember(methodName).getACall() } diff --git a/python/ql/lib/semmle/python/frameworks/Ldap.qll b/python/ql/lib/semmle/python/frameworks/Ldap.qll index 2f78c20cbe5..fddebba24f9 100644 --- a/python/ql/lib/semmle/python/frameworks/Ldap.qll +++ b/python/ql/lib/semmle/python/frameworks/Ldap.qll @@ -13,13 +13,13 @@ private import semmle.python.ApiGraphs * * See https://www.python-ldap.org/en/python-ldap-3.3.0/index.html */ -private module Ldap { +private module PythonLdap { /** * The execution of an `ldap` query. * * See https://www.python-ldap.org/en/python-ldap-3.3.0/reference/ldap.html#functions */ - private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range { + private class LdapQueryExecution extends DataFlow::CallCfgNode, Ldap::LdapExecution::Range { LdapQueryExecution() { this = API::moduleImport("ldap") diff --git a/python/ql/lib/semmle/python/frameworks/Ldap3.qll b/python/ql/lib/semmle/python/frameworks/Ldap3.qll index 6bf3d58cf99..860df4a3b84 100644 --- a/python/ql/lib/semmle/python/frameworks/Ldap3.qll +++ b/python/ql/lib/semmle/python/frameworks/Ldap3.qll @@ -15,7 +15,7 @@ private import semmle.python.ApiGraphs */ private module Ldap3 { /** The execution of an `ldap` query. */ - private class LdapQueryExecution extends DataFlow::CallCfgNode, LDAP::LdapExecution::Range { + private class LdapQueryExecution extends DataFlow::CallCfgNode, Ldap::LdapExecution::Range { LdapQueryExecution() { this = API::moduleImport("ldap3") diff --git a/python/ql/lib/semmle/python/frameworks/Libtaxii.qll b/python/ql/lib/semmle/python/frameworks/Libtaxii.qll index 44298b7c08f..5df0e7127ec 100644 --- a/python/ql/lib/semmle/python/frameworks/Libtaxii.qll +++ b/python/ql/lib/semmle/python/frameworks/Libtaxii.qll @@ -22,7 +22,7 @@ private module Libtaxii { * A call to `libtaxii.common.parse`. * When the `allow_url` parameter value is set to `True`, there is an SSRF vulnerability.. */ - private class ParseCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class ParseCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { ParseCall() { this = API::moduleImport("libtaxii").getMember("common").getMember("parse").getACall() and this.getArgByName("allow_url").getALocalSource().asExpr() = any(True t) diff --git a/python/ql/lib/semmle/python/frameworks/Peewee.qll b/python/ql/lib/semmle/python/frameworks/Peewee.qll index 189ab14876a..977428fb98b 100644 --- a/python/ql/lib/semmle/python/frameworks/Peewee.qll +++ b/python/ql/lib/semmle/python/frameworks/Peewee.qll @@ -32,7 +32,7 @@ private module Peewee { .getASubclass*() or // Ohter known subclasses, semi auto generated by using - // ```codeql + // ```ql // class DBClass extends Class, SelfRefMixin { // DBClass() { // exists(this.getLocation().getFile().getRelativePath()) and diff --git a/python/ql/lib/semmle/python/frameworks/Pycurl.qll b/python/ql/lib/semmle/python/frameworks/Pycurl.qll index 6a5fbcbcfeb..4317cfb7eca 100644 --- a/python/ql/lib/semmle/python/frameworks/Pycurl.qll +++ b/python/ql/lib/semmle/python/frameworks/Pycurl.qll @@ -36,7 +36,7 @@ private module Pycurl { * * See http://pycurl.io/docs/latest/curlobject.html#pycurl.Curl.setopt. */ - private class OutgoingRequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class OutgoingRequestCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { OutgoingRequestCall() { this = instance().getMember("setopt").getACall() and this.getArg(0).asCfgNode().(AttrNode).getName() = "URL" diff --git a/python/ql/lib/semmle/python/frameworks/Requests.qll b/python/ql/lib/semmle/python/frameworks/Requests.qll index 0c944d889d2..d32860b177e 100644 --- a/python/ql/lib/semmle/python/frameworks/Requests.qll +++ b/python/ql/lib/semmle/python/frameworks/Requests.qll @@ -28,11 +28,11 @@ private module Requests { * * See https://requests.readthedocs.io/en/latest/api/#requests.request */ - private class OutgoingRequestCall extends HTTP::Client::Request::Range, API::CallNode { + private class OutgoingRequestCall extends Http::Client::Request::Range, API::CallNode { string methodName; OutgoingRequestCall() { - methodName in [HTTP::httpVerbLower(), "request"] and + methodName in [Http::httpVerbLower(), "request"] and ( this = API::moduleImport("requests").getMember(methodName).getACall() or diff --git a/python/ql/lib/semmle/python/frameworks/RestFramework.qll b/python/ql/lib/semmle/python/frameworks/RestFramework.qll index c8f73f909c2..8406ee51e27 100644 --- a/python/ql/lib/semmle/python/frameworks/RestFramework.qll +++ b/python/ql/lib/semmle/python/frameworks/RestFramework.qll @@ -158,7 +158,7 @@ private module RestFramework { * `HTTP::Server::RequestHandler`. We only need this for the ones that doesn't have a * known route setup. */ - class RestFrameworkFunctionBasedViewWithoutKnownRoute extends HTTP::Server::RequestHandler::Range, + class RestFrameworkFunctionBasedViewWithoutKnownRoute extends Http::Server::RequestHandler::Range, PrivateDjango::DjangoRouteHandler instanceof RestFrameworkFunctionBasedView { RestFrameworkFunctionBasedViewWithoutKnownRoute() { not exists(PrivateDjango::DjangoRouteSetup setup | setup.getARequestHandler() = this) @@ -220,7 +220,7 @@ private module RestFramework { * * Use the predicate `Request::instance()` to get references to instances of `rest_framework.request.Request`. */ - abstract class InstanceSource extends PrivateDjango::DjangoImpl::Http::Request::HttpRequest::InstanceSource { + abstract class InstanceSource extends PrivateDjango::DjangoImpl::DjangoHttp::Request::HttpRequest::InstanceSource { } /** A direct instantiation of `rest_framework.request.Request`. */ @@ -296,7 +296,7 @@ private module RestFramework { } /** A direct instantiation of `rest_framework.response.Response`. */ - private class ClassInstantiation extends PrivateDjango::DjangoImpl::Http::Response::HttpResponse::InstanceSource, + private class ClassInstantiation extends PrivateDjango::DjangoImpl::DjangoHttp::Response::HttpResponse::InstanceSource, DataFlow::CallCfgNode { ClassInstantiation() { this = classRef().getACall() } @@ -320,7 +320,7 @@ private module RestFramework { */ module ApiException { /** A direct instantiation of `rest_framework.exceptions.ApiException` or subclass. */ - private class ClassInstantiation extends HTTP::Server::HttpResponse::Range, + private class ClassInstantiation extends Http::Server::HttpResponse::Range, DataFlow::CallCfgNode { string className; diff --git a/python/ql/lib/semmle/python/frameworks/Starlette.qll b/python/ql/lib/semmle/python/frameworks/Starlette.qll index e22259d560c..a105d62c5b7 100644 --- a/python/ql/lib/semmle/python/frameworks/Starlette.qll +++ b/python/ql/lib/semmle/python/frameworks/Starlette.qll @@ -85,7 +85,7 @@ module Starlette { } /** An attribute read on a `starlette.websockets.WebSocket` instance that is a `starlette.requests.URL` instance. */ - private class UrlInstances extends URL::InstanceSource { + private class UrlInstances extends Url::InstanceSource { UrlInstances() { this.(DataFlow::AttrRead).getObject() = instance() and this.(DataFlow::AttrRead).getAttributeName() = "url" @@ -98,7 +98,7 @@ module Starlette { * * See the URL part of https://www.starlette.io/websockets/. */ - module URL { + module Url { /** Gets a reference to the `starlette.requests.URL` class. */ private API::Node classRef() { result = API::moduleImport("starlette").getMember("requests").getMember("URL") @@ -159,4 +159,7 @@ module Starlette { } } } + + /** DEPRECATED: Alias for Url */ + deprecated module URL = Url; } diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib.qll index 9b5ff4c291e..38f9aca1ff0 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib.qll @@ -1877,7 +1877,7 @@ private module StdlibPrivate { API::Node http() { result = API::moduleImport("http") } /** Provides models for the `http` module. */ - module Http { + module StdlibHttp { // ------------------------------------------------------------------------- // http.server // ------------------------------------------------------------------------- @@ -1944,9 +1944,9 @@ private module StdlibPrivate { SimpleHttpServer::SimpleHttpRequestHandler::classRef(), CgiHttpServer::CgiHttpRequestHandler::classRef(), // Python 3 - Http::Server::BaseHttpRequestHandler::classRef(), - Http::Server::SimpleHttpRequestHandler::classRef(), - Http::Server::CgiHttpRequestHandler::classRef() + StdlibHttp::Server::BaseHttpRequestHandler::classRef(), + StdlibHttp::Server::SimpleHttpRequestHandler::classRef(), + StdlibHttp::Server::CgiHttpRequestHandler::classRef() ].getASubclass*() } @@ -2026,10 +2026,10 @@ private module StdlibPrivate { * * Not essential for any functionality, but provides a consistent modeling. */ - private class RequestHandlerFunc extends HTTP::Server::RequestHandler::Range { + private class RequestHandlerFunc extends Http::Server::RequestHandler::Range { RequestHandlerFunc() { this = any(HttpRequestHandlerClassDef cls).getAMethod() and - this.getName() = "do_" + HTTP::httpVerb() + this.getName() = "do_" + Http::httpVerb() } override Parameter getARoutedParameter() { none() } @@ -2064,7 +2064,7 @@ private module StdlibPrivate { * See https://github.com/python/cpython/blob/b567b9d74bd9e476a3027335873bb0508d6e450f/Lib/wsgiref/handlers.py#L137 * for how a request is processed and given to an application. */ - class WsgirefSimpleServerApplication extends HTTP::Server::RequestHandler::Range { + class WsgirefSimpleServerApplication extends Http::Server::RequestHandler::Range { WsgirefSimpleServerApplication() { exists(DataFlow::Node appArg, DataFlow::CallCfgNode setAppCall | ( @@ -2166,7 +2166,7 @@ private module StdlibPrivate { * * See https://github.com/python/cpython/blob/b567b9d74bd9e476a3027335873bb0508d6e450f/Lib/wsgiref/handlers.py#L276 */ - class WsgirefSimpleServerApplicationWriteCall extends HTTP::Server::HttpResponse::Range, + class WsgirefSimpleServerApplicationWriteCall extends Http::Server::HttpResponse::Range, DataFlow::CallCfgNode { WsgirefSimpleServerApplicationWriteCall() { this.getFunction() = writeFunction() } @@ -2180,7 +2180,7 @@ private module StdlibPrivate { /** * A return from a `WsgirefSimpleServerApplication`, which is included in the response body. */ - class WsgirefSimpleServerApplicationReturn extends HTTP::Server::HttpResponse::Range, + class WsgirefSimpleServerApplicationReturn extends Http::Server::HttpResponse::Range, DataFlow::CfgNode { WsgirefSimpleServerApplicationReturn() { exists(WsgirefSimpleServerApplication requestHandler | @@ -2267,7 +2267,7 @@ private module StdlibPrivate { } /** A method call on a HttpConnection that sends off a request */ - private class RequestCall extends HTTP::Client::Request::Range, DataFlow::MethodCallNode { + private class RequestCall extends Http::Client::Request::Range, DataFlow::MethodCallNode { RequestCall() { this.calls(instance(_), ["request", "_send_request", "putrequest"]) } DataFlow::Node getUrlArg() { result in [this.getArg(1), this.getArgByName("url")] } diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll index fa932af2673..3e0177f8a14 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib.qll @@ -30,7 +30,7 @@ private module Urllib { * See * - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.Request */ - private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class RequestCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { RequestCall() { this = API::moduleImport("urllib").getMember("request").getMember("Request").getACall() } @@ -52,7 +52,7 @@ private module Urllib { * See * - https://docs.python.org/3.9/library/urllib.request.html#urllib.request.urlopen */ - private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class UrlOpenCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { UrlOpenCall() { this = API::moduleImport("urllib").getMember("request").getMember("urlopen").getACall() } diff --git a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll index e7e119d98c1..7fc9e5ea468 100644 --- a/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll +++ b/python/ql/lib/semmle/python/frameworks/Stdlib/Urllib2.qll @@ -20,7 +20,7 @@ private module Urllib2 { * See * - https://docs.python.org/2/library/urllib2.html#urllib2.Request */ - private class RequestCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class RequestCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { RequestCall() { this = API::moduleImport("urllib2").getMember("Request").getACall() } override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] } @@ -40,7 +40,7 @@ private module Urllib2 { * See * - https://docs.python.org/2/library/urllib2.html#urllib2.urlopen */ - private class UrlOpenCall extends HTTP::Client::Request::Range, DataFlow::CallCfgNode { + private class UrlOpenCall extends Http::Client::Request::Range, DataFlow::CallCfgNode { UrlOpenCall() { this = API::moduleImport("urllib2").getMember("urlopen").getACall() } override DataFlow::Node getAUrlPart() { result in [this.getArg(0), this.getArgByName("url")] } diff --git a/python/ql/lib/semmle/python/frameworks/Tornado.qll b/python/ql/lib/semmle/python/frameworks/Tornado.qll index d66da17aa5d..77c60097cb5 100644 --- a/python/ql/lib/semmle/python/frameworks/Tornado.qll +++ b/python/ql/lib/semmle/python/frameworks/Tornado.qll @@ -99,7 +99,7 @@ private module Tornado { // TODO: This doesn't handle attribute assignment. Should be OK, but analysis is not as complete as with // points-to and `.lookup`, which would handle `post = my_post_handler` inside class def result = this.getAMethod() and - result.getName() = HTTP::httpVerbLower() + result.getName() = Http::httpVerbLower() } /** Gets a reference to this class. */ @@ -375,7 +375,7 @@ private module Tornado { } /** A tornado route setup. */ - abstract class TornadoRouteSetup extends HTTP::Server::RouteSetup::Range { + abstract class TornadoRouteSetup extends Http::Server::RouteSetup::Range { override string getFramework() { result = "Tornado" } } @@ -437,7 +437,7 @@ private module Tornado { } /** A request handler defined in a tornado RequestHandler class, that has no known route. */ - private class TornadoRequestHandlerWithoutKnownRoute extends HTTP::Server::RequestHandler::Range { + private class TornadoRequestHandlerWithoutKnownRoute extends Http::Server::RequestHandler::Range { TornadoRequestHandlerWithoutKnownRoute() { exists(TornadoModule::Web::RequestHandler::RequestHandlerClass cls | cls.getARequestHandler() = this @@ -464,7 +464,7 @@ private module Tornado { * * See https://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.redirect */ - private class TornadoRequestHandlerRedirectCall extends HTTP::Server::HttpRedirectResponse::Range, + private class TornadoRequestHandlerRedirectCall extends Http::Server::HttpRedirectResponse::Range, DataFlow::CallCfgNode { TornadoRequestHandlerRedirectCall() { this.getFunction() = TornadoModule::Web::RequestHandler::redirectMethod() @@ -486,7 +486,7 @@ private module Tornado { * * See https://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write */ - private class TornadoRequestHandlerWriteCall extends HTTP::Server::HttpResponse::Range, + private class TornadoRequestHandlerWriteCall extends Http::Server::HttpResponse::Range, DataFlow::CallCfgNode { TornadoRequestHandlerWriteCall() { this.getFunction() = TornadoModule::Web::RequestHandler::writeMethod() @@ -504,7 +504,7 @@ private module Tornado { * * See https://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.set_cookie */ - class TornadoRequestHandlerSetCookieCall extends HTTP::Server::CookieWrite::Range, + class TornadoRequestHandlerSetCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { TornadoRequestHandlerSetCookieCall() { this.calls(TornadoModule::Web::RequestHandler::instance(), "set_cookie") diff --git a/python/ql/lib/semmle/python/frameworks/Twisted.qll b/python/ql/lib/semmle/python/frameworks/Twisted.qll index c316321555e..34218c7543f 100644 --- a/python/ql/lib/semmle/python/frameworks/Twisted.qll +++ b/python/ql/lib/semmle/python/frameworks/Twisted.qll @@ -60,7 +60,7 @@ private module Twisted { } /** A method that handles incoming requests, on a `twisted.web.resource.Resource` subclass. */ - class TwistedResourceRequestHandler extends HTTP::Server::RequestHandler::Range { + class TwistedResourceRequestHandler extends Http::Server::RequestHandler::Range { TwistedResourceRequestHandler() { this = any(TwistedResourceSubclass cls).getARequestHandler() } Parameter getRequestParameter() { result = this.getArg(getRequestParamIndex(this.getName())) } @@ -176,7 +176,7 @@ private module Twisted { /** * Implicit response from returns of render methods. */ - private class TwistedResourceRenderMethodReturn extends HTTP::Server::HttpResponse::Range, + private class TwistedResourceRenderMethodReturn extends Http::Server::HttpResponse::Range, DataFlow::CfgNode { TwistedResourceRenderMethodReturn() { this.asCfgNode() = any(TwistedResourceRenderMethod meth).getAReturnValueFlowNode() @@ -194,7 +194,7 @@ private module Twisted { * * See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.server.Request.html#write */ - class TwistedRequestWriteCall extends HTTP::Server::HttpResponse::Range, DataFlow::MethodCallNode { + class TwistedRequestWriteCall extends Http::Server::HttpResponse::Range, DataFlow::MethodCallNode { TwistedRequestWriteCall() { this.calls(Request::instance(), "write") } override DataFlow::Node getBody() { @@ -211,7 +211,7 @@ private module Twisted { * * See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http.Request.html#redirect */ - class TwistedRequestRedirectCall extends HTTP::Server::HttpRedirectResponse::Range, + class TwistedRequestRedirectCall extends Http::Server::HttpRedirectResponse::Range, DataFlow::MethodCallNode { TwistedRequestRedirectCall() { this.calls(Request::instance(), "redirect") } @@ -231,7 +231,7 @@ private module Twisted { * * See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http.Request.html#addCookie */ - class TwistedRequestAddCookieCall extends HTTP::Server::CookieWrite::Range, + class TwistedRequestAddCookieCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { TwistedRequestAddCookieCall() { this.calls(Twisted::Request::instance(), "addCookie") } @@ -247,7 +247,7 @@ private module Twisted { * * See https://twistedmatrix.com/documents/21.2.0/api/twisted.web.http.Request.html#cookies */ - class TwistedRequestCookiesAppendCall extends HTTP::Server::CookieWrite::Range, + class TwistedRequestCookiesAppendCall extends Http::Server::CookieWrite::Range, DataFlow::MethodCallNode { TwistedRequestCookiesAppendCall() { exists(DataFlow::AttrRead cookiesLookup | diff --git a/python/ql/lib/semmle/python/frameworks/Urllib3.qll b/python/ql/lib/semmle/python/frameworks/Urllib3.qll index 418a135fbfc..0214dadfc10 100644 --- a/python/ql/lib/semmle/python/frameworks/Urllib3.qll +++ b/python/ql/lib/semmle/python/frameworks/Urllib3.qll @@ -49,7 +49,7 @@ private module Urllib3 { * - https://urllib3.readthedocs.io/en/stable/reference/urllib3.request.html#urllib3.request.RequestMethods * - https://urllib3.readthedocs.io/en/stable/reference/urllib3.connectionpool.html#urllib3.HTTPConnectionPool.urlopen */ - private class RequestCall extends HTTP::Client::Request::Range, API::CallNode { + private class RequestCall extends Http::Client::Request::Range, API::CallNode { RequestCall() { this = classRef() diff --git a/python/ql/lib/semmle/python/objects/TObject.qll b/python/ql/lib/semmle/python/objects/TObject.qll index ee6bcb8340d..58be33ddd91 100644 --- a/python/ql/lib/semmle/python/objects/TObject.qll +++ b/python/ql/lib/semmle/python/objects/TObject.qll @@ -334,7 +334,7 @@ predicate call3( } bindingset[self, function] -predicate method_binding( +deprecated predicate method_binding( AttrNode instantiation, ObjectInternal self, CallableObjectInternal function, PointsToContext context ) { @@ -357,7 +357,9 @@ predicate method_binding( /** Helper for method_binding */ pragma[noinline] -predicate receiver(AttrNode instantiation, PointsToContext context, ObjectInternal obj, string name) { +deprecated predicate receiver( + AttrNode instantiation, PointsToContext context, ObjectInternal obj, string name +) { PointsToInternal::pointsTo(instantiation.getObject(name), context, obj, _) } diff --git a/python/ql/lib/semmle/python/pointsto/Base.qll b/python/ql/lib/semmle/python/pointsto/Base.qll index adc2abedaa0..a3407419da2 100644 --- a/python/ql/lib/semmle/python/pointsto/Base.qll +++ b/python/ql/lib/semmle/python/pointsto/Base.qll @@ -13,7 +13,7 @@ import semmle.python.essa.SsaDefinitions private import semmle.python.types.Builtins private import semmle.python.internal.CachedStages -module BasePointsTo { +deprecated module BasePointsTo { /** INTERNAL -- Use n.refersTo(value, _, origin) instead */ pragma[noinline] predicate points_to(ControlFlowNode f, Object value, ControlFlowNode origin) { @@ -27,13 +27,13 @@ module BasePointsTo { } /** Gets the kwargs parameter (`**kwargs`). In a function definition this is always a dict. */ -predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { +deprecated predicate kwargs_points_to(ControlFlowNode f, ClassObject cls) { exists(Function func | func.getKwarg() = f.getNode()) and cls = theDictType() } /** Gets the varargs parameter (`*varargs`). In a function definition this is always a tuple. */ -predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { +deprecated predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { exists(Function func | func.getVararg() = f.getNode()) and cls = theTupleType() } @@ -45,7 +45,7 @@ predicate varargs_points_to(ControlFlowNode f, ClassObject cls) { * This exists primarily for internal use. Use getAnInferredType() instead. */ pragma[noinline] -ClassObject simple_types(Object obj) { +deprecated ClassObject simple_types(Object obj) { result = comprehension(obj.getOrigin()) or result = collection_literal(obj.getOrigin()) @@ -59,7 +59,7 @@ ClassObject simple_types(Object obj) { obj = unknownValue() and result = theUnknownType() } -private ClassObject comprehension(Expr e) { +deprecated private ClassObject comprehension(Expr e) { e instanceof ListComp and result = theListType() or e instanceof SetComp and result = theSetType() @@ -69,7 +69,7 @@ private ClassObject comprehension(Expr e) { e instanceof GeneratorExp and result = theGeneratorType() } -private ClassObject collection_literal(Expr e) { +deprecated private ClassObject collection_literal(Expr e) { e instanceof List and result = theListType() or e instanceof Set and result = theSetType() @@ -79,7 +79,7 @@ private ClassObject collection_literal(Expr e) { e instanceof Tuple and result = theTupleType() } -private int tuple_index_value(Object t, int i) { +deprecated private int tuple_index_value(Object t, int i) { result = t.(TupleNode).getElement(i).getNode().(Num).getN().toInt() or exists(Object item | @@ -89,7 +89,7 @@ private int tuple_index_value(Object t, int i) { } pragma[noinline] -int version_tuple_value(Object t) { +deprecated int version_tuple_value(Object t) { not exists(tuple_index_value(t, 1)) and result = tuple_index_value(t, 0) * 10 or not exists(tuple_index_value(t, 2)) and @@ -102,7 +102,7 @@ int version_tuple_value(Object t) { } /** Choose a version numbers that represent the extreme of supported versions. */ -private int major_minor() { +deprecated private int major_minor() { if major_version() = 3 then ( result = 33 or result = 37 @@ -113,7 +113,7 @@ private int major_minor() { } /** Compares the given tuple object to both the maximum and minimum possible sys.version_info values */ -int version_tuple_compare(Object t) { +deprecated int version_tuple_compare(Object t) { version_tuple_value(t) < major_minor() and result = -1 or version_tuple_value(t) = major_minor() and result = 0 @@ -122,7 +122,7 @@ int version_tuple_compare(Object t) { } /** Holds if `cls` is a new-style class if it were to have no explicit base classes */ -predicate baseless_is_new_style(ClassObject cls) { +deprecated predicate baseless_is_new_style(ClassObject cls) { cls.isBuiltin() or major_version() = 3 and exists(cls) @@ -160,7 +160,7 @@ private predicate class_defines_name(Class cls, string name) { } /** Gets a return value CFG node, provided that is safe to track across returns */ -ControlFlowNode safe_return_node(PyFunctionObject func) { +deprecated ControlFlowNode safe_return_node(PyFunctionObject func) { result = func.getAReturnedNode() and // Not a parameter not exists(Parameter p, SsaVariable pvar | @@ -172,7 +172,7 @@ ControlFlowNode safe_return_node(PyFunctionObject func) { } /** Holds if it can be determined from the control flow graph alone that this function can never return */ -predicate function_can_never_return(FunctionObject func) { +deprecated predicate function_can_never_return(FunctionObject func) { /* * A Python function never returns if it has no normal exits that are not dominated by a * call to a function which itself never returns. @@ -188,7 +188,9 @@ predicate function_can_never_return(FunctionObject func) { /** Hold if outer contains inner, both are contained within a test and inner is a use is a plain use or an attribute lookup */ pragma[noinline] -predicate contains_interesting_expression_within_test(ControlFlowNode outer, ControlFlowNode inner) { +deprecated predicate contains_interesting_expression_within_test( + ControlFlowNode outer, ControlFlowNode inner +) { inner.isLoad() and exists(ControlFlowNode test | outer.getAChild*() = inner and @@ -208,7 +210,7 @@ predicate test_contains(ControlFlowNode expr, ControlFlowNode use) { } /** Holds if `test` is a test (a branch), `use` is within that test and `def` is an edge from that test with `sense` */ -predicate refinement_test( +deprecated predicate refinement_test( ControlFlowNode test, ControlFlowNode use, boolean sense, PyEdgeRefinement def ) { /* @@ -224,7 +226,7 @@ predicate refinement_test( /** Holds if `f` is an import of the form `from .[...] import name` and the enclosing scope is an __init__ module */ pragma[noinline] -predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { +deprecated predicate live_import_from_dot_in_init(ImportMemberNode f, EssaVariable var) { exists(string name | import_from_dot_in_init(f.getModule(name)) and var.getSourceVariable().getName() = name and @@ -249,13 +251,13 @@ Object undefinedVariable() { py_special_objects(result, "_semmle_undefined_value /** Gets the pseudo-object representing an unknown value */ Object unknownValue() { result.asBuiltin() = Builtin::unknown() } -BuiltinCallable theTypeNewMethod() { +deprecated BuiltinCallable theTypeNewMethod() { result.asBuiltin() = theTypeType().asBuiltin().getMember("__new__") } /** Gets the `value, cls, origin` that `f` would refer to if it has not been assigned some other value */ pragma[noinline] -predicate potential_builtin_points_to( +deprecated predicate potential_builtin_points_to( NameNode f, Object value, ClassObject cls, ControlFlowNode origin ) { f.isGlobal() and @@ -269,7 +271,7 @@ predicate potential_builtin_points_to( } pragma[noinline] -predicate builtin_name_points_to(string name, Object value, ClassObject cls) { +deprecated predicate builtin_name_points_to(string name, Object value, ClassObject cls) { value = Object::builtin(name) and cls.asBuiltin() = value.asBuiltin().getClass() } @@ -331,7 +333,9 @@ module BaseFlow { } /** Points-to for syntactic elements where context is not relevant */ -predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin) { +deprecated predicate simple_points_to( + ControlFlowNode f, Object value, ClassObject cls, ControlFlowNode origin +) { kwargs_points_to(f, cls) and value = f and origin = f or varargs_points_to(f, cls) and value = f and origin = f @@ -347,7 +351,9 @@ predicate simple_points_to(ControlFlowNode f, Object value, ClassObject cls, Con * Holds if `bit` is a binary expression node with a bitwise operator. * Helper for `this_binary_expr_points_to`. */ -predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right) { +deprecated predicate bitwise_expression_node( + BinaryExprNode bit, ControlFlowNode left, ControlFlowNode right +) { exists(Operator op | op = bit.getNode().getOp() | op instanceof BitAnd or op instanceof BitOr or @@ -357,13 +363,13 @@ predicate bitwise_expression_node(BinaryExprNode bit, ControlFlowNode left, Cont right = bit.getRight() } -private Module theCollectionsAbcModule() { +deprecated private Module theCollectionsAbcModule() { result.getName() = "_abcoll" or result.getName() = "_collections_abc" } -ClassObject collectionsAbcClass(string name) { +deprecated ClassObject collectionsAbcClass(string name) { exists(Class cls | result.getPyClass() = cls and cls.getName() = name and diff --git a/python/ql/lib/semmle/python/pointsto/Filters.qll b/python/ql/lib/semmle/python/pointsto/Filters.qll index 117845d3c7f..dbf9baf5279 100644 --- a/python/ql/lib/semmle/python/pointsto/Filters.qll +++ b/python/ql/lib/semmle/python/pointsto/Filters.qll @@ -13,7 +13,7 @@ predicate hasattr(CallNode c, ControlFlowNode obj, string attr) { } /** Holds if `c` is a call to `callable(obj)`. */ -predicate is_callable(CallNode c, ControlFlowNode obj) { +deprecated predicate is_callable(CallNode c, ControlFlowNode obj) { c.getFunction().(NameNode).getId() = "callable" and obj = c.getArg(0) } @@ -26,7 +26,7 @@ predicate isinstance(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { } /** Holds if `c` is a call to `issubclass(use, cls)`. */ -predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { +deprecated predicate issubclass(CallNode fc, ControlFlowNode cls, ControlFlowNode use) { fc.getFunction().(NameNode).getId() = "issubclass" and fc.getArg(0) = use and cls = fc.getArg(1) diff --git a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll index ad86a49ba6f..a63381e5e20 100644 --- a/python/ql/lib/semmle/python/pointsto/PointsToContext.qll +++ b/python/ql/lib/semmle/python/pointsto/PointsToContext.qll @@ -122,7 +122,7 @@ private newtype TPointsToContext = } or TObjectContext(SelfInstanceInternal object) -module Context { +deprecated module Context { PointsToContext forObject(ObjectInternal object) { result = TObjectContext(object) } } diff --git a/python/ql/lib/semmle/python/protocols.qll b/python/ql/lib/semmle/python/protocols.qll index f1f7e6a3ed7..fc341dadc33 100644 --- a/python/ql/lib/semmle/python/protocols.qll +++ b/python/ql/lib/semmle/python/protocols.qll @@ -1,19 +1,19 @@ import python /** Retained for backwards compatibility use ClassObject.isIterator() instead. */ -predicate is_iterator(ClassObject c) { c.isIterator() } +deprecated predicate is_iterator(ClassObject c) { c.isIterator() } /** Retained for backwards compatibility use ClassObject.isIterable() instead. */ -predicate is_iterable(ClassObject c) { c.isIterable() } +deprecated predicate is_iterable(ClassObject c) { c.isIterable() } /** Retained for backwards compatibility use ClassObject.isCollection() instead. */ -predicate is_collection(ClassObject c) { c.isCollection() } +deprecated predicate is_collection(ClassObject c) { c.isCollection() } /** Retained for backwards compatibility use ClassObject.isMapping() instead. */ -predicate is_mapping(ClassObject c) { c.isMapping() } +deprecated predicate is_mapping(ClassObject c) { c.isMapping() } /** Retained for backwards compatibility use ClassObject.isSequence() instead. */ -predicate is_sequence(ClassObject c) { c.isSequence() } +deprecated predicate is_sequence(ClassObject c) { c.isSequence() } /** Retained for backwards compatibility use ClassObject.isContextManager() instead. */ -predicate is_context_manager(ClassObject c) { c.isContextManager() } +deprecated predicate is_context_manager(ClassObject c) { c.isContextManager() } diff --git a/python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll b/python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll index e11bc5182f0..65e662f0bc5 100644 --- a/python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll +++ b/python/ql/lib/semmle/python/security/OverlyLargeRangeQuery.qll @@ -96,7 +96,10 @@ class OverlyWideRange extends RegExpCharacterRange { toCodePoint("A") <= high or // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) + exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and + // while still being ascii + low < 128 and + high < 128 ) and // allowlist for known ranges not this = allowedWideRanges() diff --git a/python/ql/lib/semmle/python/security/dataflow/CleartextStorageCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/CleartextStorageCustomizations.qll index 3c7bc43ffa2..0ff32823d68 100644 --- a/python/ql/lib/semmle/python/security/dataflow/CleartextStorageCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/CleartextStorageCustomizations.qll @@ -56,7 +56,7 @@ module CleartextStorage { /** The data written to a cookie on a HTTP response, considered as a flow sink. */ class CookieWriteAsSink extends Sink { CookieWriteAsSink() { - exists(HTTP::Server::CookieWrite write | + exists(Http::Server::CookieWrite write | this = write.getValueArg() or this = write.getHeaderArg() diff --git a/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll index dc038ad25b0..fbab127511b 100644 --- a/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/LdapInjectionCustomizations.qll @@ -64,14 +64,14 @@ module LdapInjection { * A logging operation, considered as a flow sink. */ class LdapExecutionAsDnSink extends DnSink { - LdapExecutionAsDnSink() { this = any(LDAP::LdapExecution ldap).getBaseDn() } + LdapExecutionAsDnSink() { this = any(Ldap::LdapExecution ldap).getBaseDn() } } /** * A logging operation, considered as a flow sink. */ class LdapExecutionAsFilterSink extends FilterSink { - LdapExecutionAsFilterSink() { this = any(LDAP::LdapExecution ldap).getFilter() } + LdapExecutionAsFilterSink() { this = any(Ldap::LdapExecution ldap).getFilter() } } /** diff --git a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll index e2a9fab37a3..ea826c3b0c5 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ReflectedXSSCustomizations.qll @@ -48,7 +48,7 @@ module ReflectedXss { */ class ServerHttpResponseBodyAsSink extends Sink { ServerHttpResponseBodyAsSink() { - exists(HTTP::Server::HttpResponse response | + exists(Http::Server::HttpResponse response | response.getMimetype().toLowerCase() = "text/html" and this = response.getBody() ) diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll index b6df75addf0..e5a0be727cf 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryCustomizations.qll @@ -28,7 +28,7 @@ module ServerSideRequestForgery { /** * Gets the request this sink belongs to. */ - abstract HTTP::Client::Request getRequest(); + abstract Http::Client::Request getRequest(); } /** @@ -57,7 +57,7 @@ module ServerSideRequestForgery { /** The URL of an HTTP request, considered as a sink. */ class HttpRequestUrlAsSink extends Sink { - HTTP::Client::Request req; + Http::Client::Request req; HttpRequestUrlAsSink() { req.getAUrlPart() = this and @@ -74,7 +74,7 @@ module ServerSideRequestForgery { not req.getScope().getEnclosingModule().getName() in ["http.client", "httplib"] } - override HTTP::Client::Request getRequest() { result = req } + override Http::Client::Request getRequest() { result = req } } /** diff --git a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryQuery.qll b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryQuery.qll index 2872ca5192f..36ec416cca2 100644 --- a/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryQuery.qll +++ b/python/ql/lib/semmle/python/security/dataflow/ServerSideRequestForgeryQuery.qll @@ -42,7 +42,7 @@ class FullServerSideRequestForgeryConfiguration extends TaintTracking::Configura /** * Holds if all URL parts of `request` is fully user controlled. */ -predicate fullyControlledRequest(HTTP::Client::Request request) { +predicate fullyControlledRequest(Http::Client::Request request) { exists(FullServerSideRequestForgeryConfiguration fullConfig | forall(DataFlow::Node urlPart | urlPart = request.getAUrlPart() | fullConfig.hasFlow(_, urlPart) diff --git a/python/ql/lib/semmle/python/security/dataflow/StackTraceExposureCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/StackTraceExposureCustomizations.qll index 3596fa80f72..09f84327a63 100644 --- a/python/ql/lib/semmle/python/security/dataflow/StackTraceExposureCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/StackTraceExposureCustomizations.qll @@ -49,6 +49,6 @@ module StackTraceExposure { * The body of a HTTP response that will be returned from a server, considered as a flow sink. */ class ServerHttpResponseBodyAsSink extends Sink { - ServerHttpResponseBodyAsSink() { this = any(HTTP::Server::HttpResponse response).getBody() } + ServerHttpResponseBodyAsSink() { this = any(Http::Server::HttpResponse response).getBody() } } } diff --git a/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll b/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll index 0da2f779c8a..b4843f1a8eb 100644 --- a/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll +++ b/python/ql/lib/semmle/python/security/dataflow/UrlRedirectCustomizations.qll @@ -48,7 +48,7 @@ module UrlRedirect { */ class RedirectLocationAsSink extends Sink { RedirectLocationAsSink() { - this = any(HTTP::Server::HttpRedirectResponse e).getRedirectLocation() + this = any(Http::Server::HttpRedirectResponse e).getRedirectLocation() } } diff --git a/python/ql/lib/semmle/python/security/regexp/NfaUtils.qll b/python/ql/lib/semmle/python/security/regexp/NfaUtils.qll index 033b8aa8cfd..5112bdad11e 100644 --- a/python/ql/lib/semmle/python/security/regexp/NfaUtils.qll +++ b/python/ql/lib/semmle/python/security/regexp/NfaUtils.qll @@ -887,11 +887,10 @@ module PrefixConstruction { /** * Holds if `state` is the textually last start state for the regular expression. */ - private predicate lastStartState(State state) { + private predicate lastStartState(RelevantState state) { exists(RegExpRoot root | state = - max(State s, Location l | - s = stateInRelevantRegexp() and + max(RelevantState s, Location l | isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() @@ -963,10 +962,17 @@ module PrefixConstruction { min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) } - /** Gets a state within a regular expression that contains a candidate state. */ - pragma[noinline] - State stateInRelevantRegexp() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(result.getRepr())) + /** A state within a regular expression that contains a candidate state. */ + class RelevantState instanceof State { + RelevantState() { + exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + + /** Gets a string representation for this state in a regular expression. */ + string toString() { result = State.super.toString() } + + /** Gets the term represented by this state. */ + RegExpTerm getRepr() { result = State.super.getRepr() } } } @@ -1007,6 +1013,8 @@ module ReDoSPruning { import PrefixConstruction as Prefix + class RelevantState = Prefix::RelevantState; + /** * Predicates for testing the presence of a rejecting suffix. * @@ -1040,32 +1048,26 @@ module ReDoSPruning { * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. */ pragma[noinline] - private predicate isLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and - ( - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - ) + private predicate isLikelyRejectable(RelevantState s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) } /** * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. */ - predicate isRejectState(State s) { - s = Prefix::stateInRelevantRegexp() and not epsilonSucc*(s) = Accept(_) - } + predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } /** * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. */ pragma[noopt] - predicate hasEdgeToLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and + predicate hasEdgeToLikelyRejectable(RelevantState s) { // all edges (at least one) with some char leads to another state that is rejectable. // the `next` states might not share a common suffix, which can cause FPs. exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | @@ -1080,8 +1082,7 @@ module ReDoSPruning { * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. */ pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(State s) { - s = Prefix::stateInRelevantRegexp() and + private string hasEdgeToLikelyRejectableHelper(RelevantState s) { not hasRejectEdge(s) and not isRejectState(s) and deltaClosedChar(s, result, _) @@ -1092,9 +1093,7 @@ module ReDoSPruning { * along epsilon edges, such that there is a transition from * `prev` to `next` that the character symbol `char`. */ - predicate deltaClosedChar(State prev, string char, State next) { - prev = Prefix::stateInRelevantRegexp() and - next = Prefix::stateInRelevantRegexp() and + predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) } diff --git a/python/ql/src/Exceptions/CatchingBaseException.ql b/python/ql/src/Exceptions/CatchingBaseException.ql index 828cbf46e4b..5205af33441 100644 --- a/python/ql/src/Exceptions/CatchingBaseException.ql +++ b/python/ql/src/Exceptions/CatchingBaseException.ql @@ -13,11 +13,12 @@ */ import python +import semmle.python.ApiGraphs predicate doesnt_reraise(ExceptStmt ex) { ex.getAFlowNode().getBasicBlock().reachesExit() } predicate catches_base_exception(ExceptStmt ex) { - ex.getType().pointsTo(ClassValue::baseException()) + ex.getType() = API::builtin("BaseException").getAValueReachableFromSource().asExpr() or not exists(ex.getType()) } diff --git a/python/ql/src/Exceptions/RaisingTuple.ql b/python/ql/src/Exceptions/RaisingTuple.ql index abfe785e9cb..9116430ac18 100644 --- a/python/ql/src/Exceptions/RaisingTuple.ql +++ b/python/ql/src/Exceptions/RaisingTuple.ql @@ -10,11 +10,14 @@ */ import python +import semmle.python.dataflow.new.DataFlow -from Raise r, Value v, AstNode origin +from Raise r, DataFlow::LocalSourceNode origin where - r.getException().pointsTo(v, origin) and - v.getClass() = ClassValue::tuple() and + exists(DataFlow::Node exception | exception.asExpr() = r.getException() | + origin.flowsTo(exception) + ) and + origin.asExpr() instanceof Tuple and major_version() = 2 /* Raising a tuple is a type error in Python 3, so is handled by the IllegalRaise query. */ select r, diff --git a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql index 0ff11dfd5b0..0bd49f2c8c8 100644 --- a/python/ql/src/Exceptions/UnguardedNextInGenerator.ql +++ b/python/ql/src/Exceptions/UnguardedNextInGenerator.ql @@ -11,17 +11,22 @@ */ import python +private import semmle.python.ApiGraphs -FunctionValue iter() { result = Value::named("iter") } +API::Node iter() { result = API::builtin("iter") } -BuiltinFunctionValue next() { result = Value::named("next") } +API::Node next() { result = API::builtin("next") } + +API::Node stopIteration() { result = API::builtin("StopIteration") } predicate call_to_iter(CallNode call, EssaVariable sequence) { - sequence.getAUse() = iter().getArgumentForCall(call, 0) + call = iter().getACall().asCfgNode() and + call.getArg(0) = sequence.getAUse() } predicate call_to_next(CallNode call, ControlFlowNode iter) { - iter = next().getArgumentForCall(call, 0) + call = next().getACall().asCfgNode() and + call.getArg(0) = iter } predicate call_to_next_has_default(CallNode call) { @@ -47,7 +52,7 @@ predicate iter_not_exhausted(EssaVariable iterator) { predicate stop_iteration_handled(CallNode call) { exists(Try t | t.containsInScope(call.getNode()) and - t.getAHandler().getType().pointsTo(ClassValue::stopIteration()) + t.getAHandler().getType() = stopIteration().getAValueReachableFromSource().asExpr() ) } @@ -61,5 +66,11 @@ where ) and call.getNode().getScope().(Function).isGenerator() and not exists(Comp comp | comp.contains(call.getNode())) and - not stop_iteration_handled(call) + not stop_iteration_handled(call) and + // PEP 479 removes this concern from 3.7 onwards + // see: https://peps.python.org/pep-0479/ + // + // However, we do not know the minor version of the analyzed code (only of the extractor), + // so we only alert on Python 2. + major_version() = 2 select call, "Call to next() in a generator" diff --git a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql index 8cf3bd78037..125957e59b6 100644 --- a/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql +++ b/python/ql/src/Security/CWE-295/RequestWithoutValidation.ql @@ -15,7 +15,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.Concepts from - HTTP::Client::Request request, DataFlow::Node disablingNode, DataFlow::Node origin, string ending + Http::Client::Request request, DataFlow::Node disablingNode, DataFlow::Node origin, string ending where request.disablesCertificateValidation(disablingNode, origin) and // Showing the origin is only useful when it's a different node than the one disabling diff --git a/python/ql/src/Security/CWE-352/CSRFProtectionDisabled.ql b/python/ql/src/Security/CWE-352/CSRFProtectionDisabled.ql index 36a4e315ffc..75b161b9e5b 100644 --- a/python/ql/src/Security/CWE-352/CSRFProtectionDisabled.ql +++ b/python/ql/src/Security/CWE-352/CSRFProtectionDisabled.ql @@ -14,24 +14,24 @@ import python import semmle.python.Concepts -predicate relevantSetting(HTTP::Server::CsrfProtectionSetting s) { +predicate relevantSetting(Http::Server::CsrfProtectionSetting s) { // rule out test code as this is a common place to turn off CSRF protection. // We don't use normal `TestScope` to find test files, since we also want to match // a settings file such as `.../integration-tests/settings.py` not s.getLocation().getFile().getAbsolutePath().matches("%test%") } -predicate vulnerableSetting(HTTP::Server::CsrfProtectionSetting s) { +predicate vulnerableSetting(Http::Server::CsrfProtectionSetting s) { s.getVerificationSetting() = false and - not exists(HTTP::Server::CsrfLocalProtectionSetting p | p.csrfEnabled()) and + not exists(Http::Server::CsrfLocalProtectionSetting p | p.csrfEnabled()) and relevantSetting(s) } -from HTTP::Server::CsrfProtectionSetting setting +from Http::Server::CsrfProtectionSetting setting where vulnerableSetting(setting) and // We have seen examples of dummy projects with vulnerable settings alongside a main // project with a protecting settings file. We want to rule out this scenario, so we // require all non-test settings to be vulnerable. - forall(HTTP::Server::CsrfProtectionSetting s | relevantSetting(s) | vulnerableSetting(s)) + forall(Http::Server::CsrfProtectionSetting s | relevantSetting(s) | vulnerableSetting(s)) select setting, "Potential CSRF vulnerability due to forgery protection being disabled or weakened." diff --git a/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql b/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql index d7ad6465f77..37334fea87c 100644 --- a/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql +++ b/python/ql/src/Security/CWE-918/FullServerSideRequestForgery.ql @@ -16,7 +16,7 @@ import DataFlow::PathGraph from FullServerSideRequestForgeryConfiguration fullConfig, DataFlow::PathNode source, - DataFlow::PathNode sink, HTTP::Client::Request request + DataFlow::PathNode sink, Http::Client::Request request where request = sink.getNode().(Sink).getRequest() and fullConfig.hasFlowPath(source, sink) and diff --git a/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql b/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql index 6c28f921020..8f8d72c7d65 100644 --- a/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql +++ b/python/ql/src/Security/CWE-918/PartialServerSideRequestForgery.ql @@ -16,7 +16,7 @@ import DataFlow::PathGraph from PartialServerSideRequestForgeryConfiguration partialConfig, DataFlow::PathNode source, - DataFlow::PathNode sink, HTTP::Client::Request request + DataFlow::PathNode sink, Http::Client::Request request where request = sink.getNode().(Sink).getRequest() and partialConfig.hasFlowPath(source, sink) and diff --git a/python/ql/src/experimental/semmle/python/Concepts.qll b/python/ql/src/experimental/semmle/python/Concepts.qll index 6729bed0097..16aa54ccea5 100644 --- a/python/ql/src/experimental/semmle/python/Concepts.qll +++ b/python/ql/src/experimental/semmle/python/Concepts.qll @@ -416,7 +416,7 @@ class CsvWriter extends DataFlow::Node { * Extend this class to refine existing API models. If you want to model new APIs, * extend `Cookie::Range` instead. */ -class Cookie extends HTTP::Server::CookieWrite instanceof Cookie::Range { +class Cookie extends Http::Server::CookieWrite instanceof Cookie::Range { /** * Holds if this cookie is secure. */ @@ -441,7 +441,7 @@ module Cookie { * Extend this class to model new APIs. If you want to refine existing API models, * extend `Cookie` instead. */ - abstract class Range extends HTTP::Server::CookieWrite::Range { + abstract class Range extends Http::Server::CookieWrite::Range { /** * Holds if this cookie is secure. */ diff --git a/python/ql/src/experimental/semmle/python/frameworks/Django.qll b/python/ql/src/experimental/semmle/python/frameworks/Django.qll index 58d1c6a4abe..d9822014466 100644 --- a/python/ql/src/experimental/semmle/python/frameworks/Django.qll +++ b/python/ql/src/experimental/semmle/python/frameworks/Django.qll @@ -15,7 +15,7 @@ private module ExperimentalPrivateDjango { private module DjangoMod { API::Node http() { result = API::moduleImport("django").getMember("http") } - module Http { + module DjangoHttp { API::Node response() { result = http().getMember("response") } API::Node request() { result = http().getMember("request") } @@ -54,7 +54,7 @@ private module ExperimentalPrivateDjango { * * Use the predicate `HttpResponse::instance()` to get references to instances of `django.http.response.HttpResponse`. */ - abstract class InstanceSource extends HTTP::Server::HttpResponse::Range, DataFlow::Node { + abstract class InstanceSource extends Http::Server::HttpResponse::Range, DataFlow::Node { } /** A direct instantiation of `django.http.response.HttpResponse`. */ @@ -156,7 +156,7 @@ private module ExperimentalPrivateDjango { */ class DjangoResponseSetCookieCall extends DataFlow::MethodCallNode, Cookie::Range { DjangoResponseSetCookieCall() { - this.calls(PrivateDjango::DjangoImpl::Http::Response::HttpResponse::instance(), + this.calls(PrivateDjango::DjangoImpl::DjangoHttp::Response::HttpResponse::instance(), "set_cookie") } diff --git a/python/ql/src/meta/alerts/RequestHandlers.ql b/python/ql/src/meta/alerts/RequestHandlers.ql index 2f79a3f1b35..3f2eae0d06b 100644 --- a/python/ql/src/meta/alerts/RequestHandlers.ql +++ b/python/ql/src/meta/alerts/RequestHandlers.ql @@ -13,7 +13,7 @@ private import semmle.python.dataflow.new.DataFlow private import semmle.python.Concepts private import meta.MetaMetrics -from HTTP::Server::RequestHandler requestHandler, string title +from Http::Server::RequestHandler requestHandler, string title where not requestHandler.getLocation().getFile() instanceof IgnoredFile and if requestHandler.isMethod() diff --git a/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected b/python/ql/test/2/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected similarity index 100% rename from python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected rename to python/ql/test/2/query-tests/Exceptions/generators/UnguardedNextInGenerator.expected diff --git a/python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref b/python/ql/test/2/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref similarity index 100% rename from python/ql/test/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref rename to python/ql/test/2/query-tests/Exceptions/generators/UnguardedNextInGenerator.qlref diff --git a/python/ql/test/query-tests/Exceptions/generators/test.py b/python/ql/test/2/query-tests/Exceptions/generators/test.py similarity index 100% rename from python/ql/test/query-tests/Exceptions/generators/test.py rename to python/ql/test/2/query-tests/Exceptions/generators/test.py diff --git a/python/ql/test/2/query-tests/Exceptions/raising/RaisingTuple.expected b/python/ql/test/2/query-tests/Exceptions/raising/RaisingTuple.expected index f95dd5defde..7200c147ea9 100644 --- a/python/ql/test/2/query-tests/Exceptions/raising/RaisingTuple.expected +++ b/python/ql/test/2/query-tests/Exceptions/raising/RaisingTuple.expected @@ -1,3 +1,3 @@ -| test.py:8:5:8:12 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:7:10:7:29 | Tuple | a tuple | -| test.py:11:5:11:32 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:11:12:11:31 | Tuple | a tuple | -| test.py:15:5:15:23 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:14:10:14:19 | Tuple | a tuple | +| test.py:8:5:8:12 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:7:10:7:29 | ControlFlowNode for Tuple | a tuple | +| test.py:11:5:11:32 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:11:12:11:31 | ControlFlowNode for Tuple | a tuple | +| test.py:15:5:15:23 | Raise | Raising $@ will result in the first element (recursively) being raised and all other elements being discarded. | test.py:14:10:14:19 | ControlFlowNode for Tuple | a tuple | diff --git a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py index 1962bf2889e..92b466cd25a 100644 --- a/python/ql/test/experimental/dataflow/coverage/argumentPassing.py +++ b/python/ql/test/experimental/dataflow/coverage/argumentPassing.py @@ -94,7 +94,7 @@ def with_multiple_kw_args(a, b, c): def test_multiple_kw_args(): with_multiple_kw_args(b=arg2, c=arg3, a=arg1) #$ arg1 arg2 arg3 with_multiple_kw_args(arg1, *(arg2,), arg3) #$ arg1 MISSING: arg2 arg3 - with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2) #$ arg1 arg3 func=with_multiple_kw_args MISSING: arg2 + with_multiple_kw_args(arg1, **{"c": arg3}, b=arg2) #$ arg1 arg2 arg3 func=with_multiple_kw_args MISSING: with_multiple_kw_args(**{"b": arg2}, **{"c": arg3}, **{"a": arg1}) #$ arg1 arg2 arg3 func=with_multiple_kw_args diff --git a/python/ql/test/experimental/meta/ConceptsTest.qll b/python/ql/test/experimental/meta/ConceptsTest.qll index c792a7005b4..9690b7b1497 100644 --- a/python/ql/test/experimental/meta/ConceptsTest.qll +++ b/python/ql/test/experimental/meta/ConceptsTest.qll @@ -239,7 +239,7 @@ class HttpServerRouteSetupTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(HTTP::Server::RouteSetup setup | + exists(Http::Server::RouteSetup setup | location = setup.getLocation() and element = setup.toString() and ( @@ -261,14 +261,14 @@ class HttpServerRequestHandlerTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and ( - exists(HTTP::Server::RequestHandler handler | + exists(Http::Server::RequestHandler handler | location = handler.getLocation() and element = handler.toString() and value = "" and tag = "requestHandler" ) or - exists(HTTP::Server::RequestHandler handler, Parameter param | + exists(Http::Server::RequestHandler handler, Parameter param | param = handler.getARoutedParameter() and location = param.getLocation() and element = param.toString() and @@ -299,21 +299,21 @@ class HttpServerHttpResponseTest extends InlineExpectationsTest { // we need to do this step since we expect subclasses could override getARelevantTag tag = getARelevantTag() and ( - exists(HTTP::Server::HttpResponse response | + exists(Http::Server::HttpResponse response | location = response.getLocation() and element = response.toString() and value = "" and tag = "HttpResponse" ) or - exists(HTTP::Server::HttpResponse response | + exists(Http::Server::HttpResponse response | location = response.getLocation() and element = response.toString() and value = prettyNodeForInlineTest(response.getBody()) and tag = "responseBody" ) or - exists(HTTP::Server::HttpResponse response | + exists(Http::Server::HttpResponse response | location = response.getLocation() and element = response.toString() and // Ensure that an expectation value such as "mimetype=text/html; charset=utf-8" is parsed as a @@ -338,14 +338,14 @@ class HttpServerHttpRedirectResponseTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and ( - exists(HTTP::Server::HttpRedirectResponse redirect | + exists(Http::Server::HttpRedirectResponse redirect | location = redirect.getLocation() and element = redirect.toString() and value = "" and tag = "HttpRedirectResponse" ) or - exists(HTTP::Server::HttpRedirectResponse redirect | + exists(Http::Server::HttpRedirectResponse redirect | location = redirect.getLocation() and element = redirect.toString() and value = prettyNodeForInlineTest(redirect.getRedirectLocation()) and @@ -364,7 +364,7 @@ class HttpServerCookieWriteTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(HTTP::Server::CookieWrite cookieWrite | + exists(Http::Server::CookieWrite cookieWrite | location = cookieWrite.getLocation() and ( element = cookieWrite.toString() and @@ -519,7 +519,7 @@ class HttpClientRequestTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(HTTP::Client::Request req, DataFlow::Node url | + exists(Http::Client::Request req, DataFlow::Node url | url = req.getAUrlPart() and location = url.getLocation() and element = url.toString() and @@ -528,7 +528,7 @@ class HttpClientRequestTest extends InlineExpectationsTest { ) or exists(location.getFile().getRelativePath()) and - exists(HTTP::Client::Request req | + exists(Http::Client::Request req | req.disablesCertificateValidation(_, _) and location = req.getLocation() and element = req.toString() and @@ -545,7 +545,7 @@ class CsrfProtectionSettingTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(HTTP::Server::CsrfProtectionSetting setting | + exists(Http::Server::CsrfProtectionSetting setting | location = setting.getLocation() and element = setting.toString() and value = setting.getVerificationSetting().toString() and @@ -561,7 +561,7 @@ class CsrfLocalProtectionSettingTest extends InlineExpectationsTest { override predicate hasActualResult(Location location, string element, string tag, string value) { exists(location.getFile().getRelativePath()) and - exists(HTTP::Server::CsrfLocalProtectionSetting p | + exists(Http::Server::CsrfLocalProtectionSetting p | location = p.getLocation() and element = p.toString() and value = p.getRequestHandler().getName().toString() and diff --git a/ql/ql/src/codeql_ql/style/MisspellingQuery.qll b/ql/ql/src/codeql_ql/style/MisspellingQuery.qll index 822a1cfcb07..ad1fd800fb3 100644 --- a/ql/ql/src/codeql_ql/style/MisspellingQuery.qll +++ b/ql/ql/src/codeql_ql/style/MisspellingQuery.qll @@ -1,6 +1,6 @@ import ql private import NodeName -private import TypoDatabase +private import codeql.typos.TypoDatabase predicate misspelling(string wrong, string right, string mistake) { mistake = "common misspelling" and diff --git a/ql/ql/src/codeql_ql/style/NodeName.qll b/ql/ql/src/codeql_ql/style/NodeName.qll index af1d10076dd..e5384864f92 100644 --- a/ql/ql/src/codeql_ql/style/NodeName.qll +++ b/ql/ql/src/codeql_ql/style/NodeName.qll @@ -20,7 +20,7 @@ string getName(AstNode node, string kind) { result = node.(NewType).getName() and kind = "newtype" or - result = node.(VarDecl).getName() and + result = node.(VarDef).getName() and kind = "variable" and not node = any(FieldDecl f).getVarDecl() or diff --git a/ql/ql/src/codeql_ql/style/TypoDatabase.qll b/ql/ql/src/codeql_ql/style/TypoDatabase.qll index a41f003a8c0..f480b9ebc8b 100644 --- a/ql/ql/src/codeql_ql/style/TypoDatabase.qll +++ b/ql/ql/src/codeql_ql/style/TypoDatabase.qll @@ -1,9035 +1,4 @@ -/** - * Holds if `wrong` is a common misspelling of `right`. - * - * This predicate was automatically generated by - * python buildutils-internal/scripts/generate-typos.py - * which uses http://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines (with custom additions) on 2018-01-19. - * - * This file is available under the Creative Commons Attribution-ShareAlike License - * (https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License). - */ -predicate typos(string wrong, string right) { - wrong = "abandonned" and right = "abandoned" - or - wrong = "abbout" and right = "about" - or - wrong = "aberation" and right = "aberration" - or - wrong = "abilityes" and right = "abilities" - or - wrong = "abilties" and right = "abilities" - or - wrong = "abilty" and right = "ability" - or - wrong = "abondon" and right = "abandon" - or - wrong = "abondoned" and right = "abandoned" - or - wrong = "abondoning" and right = "abandoning" - or - wrong = "abondons" and right = "abandons" - or - wrong = "aborigene" and right = "aborigine" - or - wrong = "abortificant" and right = "abortifacient" - or - wrong = "abotu" and right = "about" - or - wrong = "abreviate" and right = "abbreviate" - or - wrong = "abreviated" and right = "abbreviated" - or - wrong = "abreviation" and right = "abbreviation" - or - wrong = "abritrary" and right = "arbitrary" - or - wrong = "absail" and right = "abseil" - or - wrong = "absailing" and right = "abseiling" - or - wrong = "abscence" and right = "absence" - or - wrong = "absense" and right = "absence" - or - wrong = "absolutly" and right = "absolutely" - or - wrong = "absorbsion" and right = "absorption" - or - wrong = "absorbtion" and right = "absorption" - or - wrong = "abudance" and right = "abundance" - or - wrong = "abundacies" and right = "abundances" - or - wrong = "abundancies" and right = "abundances" - or - wrong = "abundunt" and right = "abundant" - or - wrong = "abutts" and right = "abuts" - or - wrong = "acadamy" and right = "academy" - or - wrong = "acadmic" and right = "academic" - or - wrong = "accademic" and right = "academic" - or - wrong = "accademy" and right = "academy" - or - wrong = "acccess" and right = "access" - or - wrong = "acccused" and right = "accused" - or - wrong = "accelleration" and right = "acceleration" - or - wrong = "accension" and right = "accession" - or - wrong = "accension" and right = "ascension" - or - wrong = "acceptence" and right = "acceptance" - or - wrong = "acceptible" and right = "acceptable" - or - wrong = "accesories" and right = "accessories" - or - wrong = "accessable" and right = "accessible" - or - wrong = "accidant" and right = "accident" - or - wrong = "accidentaly" and right = "accidentally" - or - wrong = "accidently" and right = "accidentally" - or - wrong = "acclimitization" and right = "acclimatization" - or - wrong = "accomadate" and right = "accommodate" - or - wrong = "accomadated" and right = "accommodated" - or - wrong = "accomadates" and right = "accommodates" - or - wrong = "accomadating" and right = "accommodating" - or - wrong = "accomadation" and right = "accommodation" - or - wrong = "accomadations" and right = "accommodations" - or - wrong = "accomdate" and right = "accommodate" - or - wrong = "accomodate" and right = "accommodate" - or - wrong = "accomodated" and right = "accommodated" - or - wrong = "accomodates" and right = "accommodates" - or - wrong = "accomodating" and right = "accommodating" - or - wrong = "accomodation" and right = "accommodation" - or - wrong = "accomodations" and right = "accommodations" - or - wrong = "accompanyed" and right = "accompanied" - or - wrong = "accordeon" and right = "accordion" - or - wrong = "accordian" and right = "accordion" - or - wrong = "accoring" and right = "according" - or - wrong = "accoustic" and right = "acoustic" - or - wrong = "accquainted" and right = "acquainted" - or - wrong = "accrediation" and right = "accreditation" - or - wrong = "accredidation" and right = "accreditation" - or - wrong = "accross" and right = "across" - or - wrong = "accussed" and right = "accused" - or - wrong = "acedemic" and right = "academic" - or - wrong = "acheive" and right = "achieve" - or - wrong = "acheived" and right = "achieved" - or - wrong = "acheivement" and right = "achievement" - or - wrong = "acheivements" and right = "achievements" - or - wrong = "acheives" and right = "achieves" - or - wrong = "acheiving" and right = "achieving" - or - wrong = "acheivment" and right = "achievement" - or - wrong = "acheivments" and right = "achievements" - or - wrong = "achievment" and right = "achievement" - or - wrong = "achievments" and right = "achievements" - or - wrong = "achive" and right = "achieve" - or - wrong = "achive" and right = "archive" - or - wrong = "achived" and right = "achieved" - or - wrong = "achived" and right = "archived" - or - wrong = "achivement" and right = "achievement" - or - wrong = "achivements" and right = "achievements" - or - wrong = "acident" and right = "accident" - or - wrong = "acknowldeged" and right = "acknowledged" - or - wrong = "acknowledgeing" and right = "acknowledging" - or - wrong = "ackward" and right = "awkward" - or - wrong = "ackward" and right = "backward" - or - wrong = "acommodate" and right = "accommodate" - or - wrong = "acomplish" and right = "accomplish" - or - wrong = "acomplished" and right = "accomplished" - or - wrong = "acomplishment" and right = "accomplishment" - or - wrong = "acomplishments" and right = "accomplishments" - or - wrong = "acording" and right = "according" - or - wrong = "acordingly" and right = "accordingly" - or - wrong = "acquaintence" and right = "acquaintance" - or - wrong = "acquaintences" and right = "acquaintances" - or - wrong = "acquiantence" and right = "acquaintance" - or - wrong = "acquiantences" and right = "acquaintances" - or - wrong = "acquited" and right = "acquitted" - or - wrong = "activites" and right = "activities" - or - wrong = "activly" and right = "actively" - or - wrong = "actualy" and right = "actually" - or - wrong = "acuracy" and right = "accuracy" - or - wrong = "acused" and right = "accused" - or - wrong = "acustom" and right = "accustom" - or - wrong = "acustommed" and right = "accustomed" - or - wrong = "adapater" and right = "adapter" - or - wrong = "adavanced" and right = "advanced" - or - wrong = "adbandon" and right = "abandon" - or - wrong = "addional" and right = "additional" - or - wrong = "addionally" and right = "additionally" - or - wrong = "additinally" and right = "additionally" - or - wrong = "additionaly" and right = "additionally" - or - wrong = "additonal" and right = "additional" - or - wrong = "additonally" and right = "additionally" - or - wrong = "addmission" and right = "admission" - or - wrong = "addopt" and right = "adopt" - or - wrong = "addopted" and right = "adopted" - or - wrong = "addoptive" and right = "adoptive" - or - wrong = "addres" and right = "adders" - or - wrong = "addres" and right = "address" - or - wrong = "addresable" and right = "addressable" - or - wrong = "addresed" and right = "addressed" - or - wrong = "addresing" and right = "addressing" - or - wrong = "addressess" and right = "addresses" - or - wrong = "addtion" and right = "addition" - or - wrong = "addtional" and right = "additional" - or - wrong = "adecuate" and right = "adequate" - or - wrong = "adequit" and right = "adequate" - or - wrong = "adhearing" and right = "adhering" - or - wrong = "adherance" and right = "adherence" - or - wrong = "admendment" and right = "amendment" - or - wrong = "admininistrative" and right = "administrative" - or - wrong = "adminstered" and right = "administered" - or - wrong = "adminstrate" and right = "administrate" - or - wrong = "adminstration" and right = "administration" - or - wrong = "adminstrative" and right = "administrative" - or - wrong = "adminstrator" and right = "administrator" - or - wrong = "admissability" and right = "admissibility" - or - wrong = "admissable" and right = "admissible" - or - wrong = "admited" and right = "admitted" - or - wrong = "admitedly" and right = "admittedly" - or - wrong = "adn" and right = "and" - or - wrong = "adolecent" and right = "adolescent" - or - wrong = "adquire" and right = "acquire" - or - wrong = "adquired" and right = "acquired" - or - wrong = "adquires" and right = "acquires" - or - wrong = "adquiring" and right = "acquiring" - or - wrong = "adres" and right = "address" - or - wrong = "adresable" and right = "addressable" - or - wrong = "adresing" and right = "addressing" - or - wrong = "adress" and right = "address" - or - wrong = "adressable" and right = "addressable" - or - wrong = "adressed" and right = "addressed" - or - wrong = "adressing" and right = "addressing" - or - wrong = "adressing" and right = "dressing" - or - wrong = "adventrous" and right = "adventurous" - or - wrong = "advertisment" and right = "advertisement" - or - wrong = "advertisments" and right = "advertisements" - or - wrong = "advesary" and right = "adversary" - or - wrong = "adviced" and right = "advised" - or - wrong = "aeriel" and right = "aerial" - or - wrong = "aeriels" and right = "aerials" - or - wrong = "afair" and right = "affair" - or - wrong = "afficianados" and right = "aficionados" - or - wrong = "afficionado" and right = "aficionado" - or - wrong = "afficionados" and right = "aficionados" - or - wrong = "affilate" and right = "affiliate" - or - wrong = "affilliate" and right = "affiliate" - or - wrong = "affort" and right = "afford" - or - wrong = "affort" and right = "effort" - or - wrong = "aforememtioned" and right = "aforementioned" - or - wrong = "againnst" and right = "against" - or - wrong = "agains" and right = "against" - or - wrong = "agaisnt" and right = "against" - or - wrong = "aganist" and right = "against" - or - wrong = "aggaravates" and right = "aggravates" - or - wrong = "aggreed" and right = "agreed" - or - wrong = "aggreement" and right = "agreement" - or - wrong = "aggregious" and right = "egregious" - or - wrong = "aggresive" and right = "aggressive" - or - wrong = "agian" and right = "again" - or - wrong = "agianst" and right = "against" - or - wrong = "agin" and right = "again" - or - wrong = "agina" and right = "again" - or - wrong = "agina" and right = "angina" - or - wrong = "aginst" and right = "against" - or - wrong = "agravate" and right = "aggravate" - or - wrong = "agre" and right = "agree" - or - wrong = "agred" and right = "agreed" - or - wrong = "agreeement" and right = "agreement" - or - wrong = "agreemnt" and right = "agreement" - or - wrong = "agregate" and right = "aggregate" - or - wrong = "agregates" and right = "aggregates" - or - wrong = "agreing" and right = "agreeing" - or - wrong = "agression" and right = "aggression" - or - wrong = "agressive" and right = "aggressive" - or - wrong = "agressively" and right = "aggressively" - or - wrong = "agressor" and right = "aggressor" - or - wrong = "agricultue" and right = "agriculture" - or - wrong = "agriculure" and right = "agriculture" - or - wrong = "agricuture" and right = "agriculture" - or - wrong = "agrieved" and right = "aggrieved" - or - wrong = "agrument" and right = "argument" - or - wrong = "agruments" and right = "arguments" - or - wrong = "ahev" and right = "have" - or - wrong = "ahppen" and right = "happen" - or - wrong = "ahve" and right = "have" - or - wrong = "aicraft" and right = "aircraft" - or - wrong = "aiport" and right = "airport" - or - wrong = "airbourne" and right = "airborne" - or - wrong = "aircaft" and right = "aircraft" - or - wrong = "aircrafts" and right = "aircraft" - or - wrong = "airporta" and right = "airports" - or - wrong = "airrcraft" and right = "aircraft" - or - wrong = "aisian" and right = "asian" - or - wrong = "albiet" and right = "albeit" - or - wrong = "alchohol" and right = "alcohol" - or - wrong = "alchoholic" and right = "alcoholic" - or - wrong = "alchol" and right = "alcohol" - or - wrong = "alcholic" and right = "alcoholic" - or - wrong = "alcohal" and right = "alcohol" - or - wrong = "alcoholical" and right = "alcoholic" - or - wrong = "aledge" and right = "allege" - or - wrong = "aledged" and right = "alleged" - or - wrong = "aledges" and right = "alleges" - or - wrong = "alege" and right = "allege" - or - wrong = "aleged" and right = "alleged" - or - wrong = "alegience" and right = "allegiance" - or - wrong = "algebraical" and right = "algebraic" - or - wrong = "algorhitms" and right = "algorithms" - or - wrong = "algoritm" and right = "algorithm" - or - wrong = "algoritms" and right = "algorithms" - or - wrong = "alientating" and right = "alienating" - or - wrong = "alledge" and right = "allege" - or - wrong = "alledged" and right = "alleged" - or - wrong = "alledgedly" and right = "allegedly" - or - wrong = "alledges" and right = "alleges" - or - wrong = "allegedely" and right = "allegedly" - or - wrong = "allegedy" and right = "allegedly" - or - wrong = "allegely" and right = "allegedly" - or - wrong = "allegence" and right = "allegiance" - or - wrong = "allegience" and right = "allegiance" - or - wrong = "allign" and right = "align" - or - wrong = "alligned" and right = "aligned" - or - wrong = "alliviate" and right = "alleviate" - or - wrong = "allopone" and right = "allophone" - or - wrong = "allopones" and right = "allophones" - or - wrong = "allready" and right = "already" - or - wrong = "allthough" and right = "although" - or - wrong = "alltogether" and right = "altogether" - or - wrong = "almsot" and right = "almost" - or - wrong = "alochol" and right = "alcohol" - or - wrong = "alomst" and right = "almost" - or - wrong = "alot" and right = "allot" - or - wrong = "alotted" and right = "allotted" - or - wrong = "alowed" and right = "allowed" - or - wrong = "alowing" and right = "allowing" - or - wrong = "alreayd" and right = "already" - or - wrong = "alse" and right = "else" - or - wrong = "alsot" and right = "also" - or - wrong = "alternitives" and right = "alternatives" - or - wrong = "altho" and right = "although" - or - wrong = "althought" and right = "although" - or - wrong = "altough" and right = "although" - or - wrong = "alusion" and right = "allusion" - or - wrong = "alusion" and right = "illusion" - or - wrong = "alwasy" and right = "always" - or - wrong = "alwyas" and right = "always" - or - wrong = "amalgomated" and right = "amalgamated" - or - wrong = "amatuer" and right = "amateur" - or - wrong = "amature" and right = "amateur" - or - wrong = "amature" and right = "armature" - or - wrong = "amendmant" and right = "amendment" - or - wrong = "amercia" and right = "america" - or - wrong = "amerliorate" and right = "ameliorate" - or - wrong = "amke" and right = "make" - or - wrong = "amking" and right = "making" - or - wrong = "ammend" and right = "amend" - or - wrong = "ammended" and right = "amended" - or - wrong = "ammendment" and right = "amendment" - or - wrong = "ammendments" and right = "amendments" - or - wrong = "ammount" and right = "amount" - or - wrong = "ammused" and right = "amused" - or - wrong = "amoung" and right = "among" - or - wrong = "amoungst" and right = "amongst" - or - wrong = "amung" and right = "among" - or - wrong = "amunition" and right = "ammunition" - or - wrong = "analagous" and right = "analogous" - or - wrong = "analitic" and right = "analytic" - or - wrong = "analogeous" and right = "analogous" - or - wrong = "anarchim" and right = "anarchism" - or - wrong = "anarchistm" and right = "anarchism" - or - wrong = "anbd" and right = "and" - or - wrong = "ancestory" and right = "ancestry" - or - wrong = "ancilliary" and right = "ancillary" - or - wrong = "andd" and right = "and" - or - wrong = "androgenous" and right = "androgynous" - or - wrong = "androgeny" and right = "androgyny" - or - wrong = "anihilation" and right = "annihilation" - or - wrong = "aniversary" and right = "anniversary" - or - wrong = "annoint" and right = "anoint" - or - wrong = "annointed" and right = "anointed" - or - wrong = "annointing" and right = "anointing" - or - wrong = "annoints" and right = "anoints" - or - wrong = "annouced" and right = "announced" - or - wrong = "annualy" and right = "annually" - or - wrong = "annuled" and right = "annulled" - or - wrong = "anohter" and right = "another" - or - wrong = "anomolies" and right = "anomalies" - or - wrong = "anomolous" and right = "anomalous" - or - wrong = "anomoly" and right = "anomaly" - or - wrong = "anonimity" and right = "anonymity" - or - wrong = "anounced" and right = "announced" - or - wrong = "anouncement" and right = "announcement" - or - wrong = "ansalisation" and right = "nasalisation" - or - wrong = "ansalization" and right = "nasalization" - or - wrong = "ansestors" and right = "ancestors" - or - wrong = "antartic" and right = "antarctic" - or - wrong = "anthromorphization" and right = "anthropomorphization" - or - wrong = "anthropolgist" and right = "anthropologist" - or - wrong = "anthropolgy" and right = "anthropology" - or - wrong = "anual" and right = "annual" - or - wrong = "anulled" and right = "annulled" - or - wrong = "anwsered" and right = "answered" - or - wrong = "anyhwere" and right = "anywhere" - or - wrong = "anytying" and right = "anything" - or - wrong = "aparent" and right = "apparent" - or - wrong = "aparment" and right = "apartment" - or - wrong = "apenines" and right = "apennines" - or - wrong = "aplication" and right = "application" - or - wrong = "aplied" and right = "applied" - or - wrong = "apolegetics" and right = "apologetics" - or - wrong = "apon" and right = "apron" - or - wrong = "apon" and right = "upon" - or - wrong = "apparant" and right = "apparent" - or - wrong = "apparantly" and right = "apparently" - or - wrong = "appart" and right = "apart" - or - wrong = "appartment" and right = "apartment" - or - wrong = "appartments" and right = "apartments" - or - wrong = "appealling" and right = "appalling" - or - wrong = "appealling" and right = "appealing" - or - wrong = "appeareance" and right = "appearance" - or - wrong = "appearence" and right = "appearance" - or - wrong = "appearences" and right = "appearances" - or - wrong = "appenines" and right = "apennines" - or - wrong = "apperance" and right = "appearance" - or - wrong = "apperances" and right = "appearances" - or - wrong = "appereance" and right = "appearance" - or - wrong = "appereances" and right = "appearances" - or - wrong = "applicaiton" and right = "application" - or - wrong = "applicaitons" and right = "applications" - or - wrong = "appologies" and right = "apologies" - or - wrong = "appology" and right = "apology" - or - wrong = "apprearance" and right = "appearance" - or - wrong = "apprieciate" and right = "appreciate" - or - wrong = "approachs" and right = "approaches" - or - wrong = "appropiate" and right = "appropriate" - or - wrong = "appropraite" and right = "appropriate" - or - wrong = "appropropiate" and right = "appropriate" - or - wrong = "approproximate" and right = "approximate" - or - wrong = "approxamately" and right = "approximately" - or - wrong = "approxiately" and right = "approximately" - or - wrong = "approximitely" and right = "approximately" - or - wrong = "aprehensive" and right = "apprehensive" - or - wrong = "apropriate" and right = "appropriate" - or - wrong = "aproval" and right = "approval" - or - wrong = "aproximate" and right = "approximate" - or - wrong = "aproximately" and right = "approximately" - or - wrong = "aquaduct" and right = "aqueduct" - or - wrong = "aquaintance" and right = "acquaintance" - or - wrong = "aquainted" and right = "acquainted" - or - wrong = "aquiantance" and right = "acquaintance" - or - wrong = "aquire" and right = "acquire" - or - wrong = "aquired" and right = "acquired" - or - wrong = "aquiring" and right = "acquiring" - or - wrong = "aquisition" and right = "acquisition" - or - wrong = "aquitted" and right = "acquitted" - or - wrong = "aranged" and right = "arranged" - or - wrong = "arangement" and right = "arrangement" - or - wrong = "arbitarily" and right = "arbitrarily" - or - wrong = "arbitary" and right = "arbitrary" - or - wrong = "archaelogical" and right = "archaeological" - or - wrong = "archaelogists" and right = "archaeologists" - or - wrong = "archaelogy" and right = "archaeology" - or - wrong = "archaoelogy" and right = "archaeology" - or - wrong = "archaoelogy" and right = "archeology" - or - wrong = "archaology" and right = "archaeology" - or - wrong = "archaology" and right = "archeology" - or - wrong = "archeaologist" and right = "archaeologist" - or - wrong = "archeaologist" and right = "archeologist" - or - wrong = "archeaologists" and right = "archaeologists" - or - wrong = "archeaologists" and right = "archeologists" - or - wrong = "archetect" and right = "architect" - or - wrong = "archetects" and right = "architects" - or - wrong = "archetectural" and right = "architectural" - or - wrong = "archetecturally" and right = "architecturally" - or - wrong = "archetecture" and right = "architecture" - or - wrong = "archiac" and right = "archaic" - or - wrong = "archictect" and right = "architect" - or - wrong = "archimedian" and right = "archimedean" - or - wrong = "architecht" and right = "architect" - or - wrong = "architechturally" and right = "architecturally" - or - wrong = "architechture" and right = "architecture" - or - wrong = "architechtures" and right = "architectures" - or - wrong = "architectual" and right = "architectural" - or - wrong = "archtype" and right = "archetype" - or - wrong = "archtypes" and right = "archetypes" - or - wrong = "aready" and right = "already" - or - wrong = "areodynamics" and right = "aerodynamics" - or - wrong = "argubly" and right = "arguably" - or - wrong = "arguement" and right = "argument" - or - wrong = "arguements" and right = "arguments" - or - wrong = "arised" and right = "arose" - or - wrong = "arival" and right = "arrival" - or - wrong = "armamant" and right = "armament" - or - wrong = "armistace" and right = "armistice" - or - wrong = "arogant" and right = "arrogant" - or - wrong = "arogent" and right = "arrogant" - or - wrong = "aroud" and right = "around" - or - wrong = "arrangment" and right = "arrangement" - or - wrong = "arrangments" and right = "arrangements" - or - wrong = "arrengement" and right = "arrangement" - or - wrong = "arrengements" and right = "arrangements" - or - wrong = "arround" and right = "around" - or - wrong = "artcile" and right = "article" - or - wrong = "artical" and right = "article" - or - wrong = "artice" and right = "article" - or - wrong = "articel" and right = "article" - or - wrong = "artifical" and right = "artificial" - or - wrong = "artifically" and right = "artificially" - or - wrong = "artillary" and right = "artillery" - or - wrong = "arund" and right = "around" - or - wrong = "asetic" and right = "ascetic" - or - wrong = "asign" and right = "assign" - or - wrong = "aslo" and right = "also" - or - wrong = "asnyc" and right = "async" - or - wrong = "asociated" and right = "associated" - or - wrong = "asorbed" and right = "absorbed" - or - wrong = "asphyxation" and right = "asphyxiation" - or - wrong = "assasin" and right = "assassin" - or - wrong = "assasinate" and right = "assassinate" - or - wrong = "assasinated" and right = "assassinated" - or - wrong = "assasinates" and right = "assassinates" - or - wrong = "assasination" and right = "assassination" - or - wrong = "assasinations" and right = "assassinations" - or - wrong = "assasined" and right = "assassinated" - or - wrong = "assasins" and right = "assassins" - or - wrong = "assassintation" and right = "assassination" - or - wrong = "assemple" and right = "assemble" - or - wrong = "assertation" and right = "assertion" - or - wrong = "asside" and right = "aside" - or - wrong = "assisnate" and right = "assassinate" - or - wrong = "assit" and right = "assist" - or - wrong = "assitant" and right = "assistant" - or - wrong = "assocation" and right = "association" - or - wrong = "assoicate" and right = "associate" - or - wrong = "assoicated" and right = "associated" - or - wrong = "assoicates" and right = "associates" - or - wrong = "assosication" and right = "assassination" - or - wrong = "asssassans" and right = "assassins" - or - wrong = "assualt" and right = "assault" - or - wrong = "assualted" and right = "assaulted" - or - wrong = "assymetric" and right = "asymmetric" - or - wrong = "assymetrical" and right = "asymmetrical" - or - wrong = "asteriod" and right = "asteroid" - or - wrong = "asthetic" and right = "aesthetic" - or - wrong = "asthetical" and right = "aesthetical" - or - wrong = "asthetically" and right = "aesthetically" - or - wrong = "asume" and right = "assume" - or - wrong = "atain" and right = "attain" - or - wrong = "atempting" and right = "attempting" - or - wrong = "atheistical" and right = "atheistic" - or - wrong = "athenean" and right = "athenian" - or - wrong = "atheneans" and right = "athenians" - or - wrong = "athiesm" and right = "atheism" - or - wrong = "athiest" and right = "atheist" - or - wrong = "atorney" and right = "attorney" - or - wrong = "atribute" and right = "attribute" - or - wrong = "atributed" and right = "attributed" - or - wrong = "atributes" and right = "attributes" - or - wrong = "attaindre" and right = "attainder" - or - wrong = "attaindre" and right = "attained" - or - wrong = "attemp" and right = "attempt" - or - wrong = "attemped" and right = "attempted" - or - wrong = "attemt" and right = "attempt" - or - wrong = "attemted" and right = "attempted" - or - wrong = "attemting" and right = "attempting" - or - wrong = "attemts" and right = "attempts" - or - wrong = "attendence" and right = "attendance" - or - wrong = "attendent" and right = "attendant" - or - wrong = "attendents" and right = "attendants" - or - wrong = "attened" and right = "attended" - or - wrong = "attension" and right = "attention" - or - wrong = "attitide" and right = "attitude" - or - wrong = "attributred" and right = "attributed" - or - wrong = "attrocities" and right = "atrocities" - or - wrong = "audeince" and right = "audience" - or - wrong = "auromated" and right = "automated" - or - wrong = "austrailia" and right = "australia" - or - wrong = "austrailian" and right = "australian" - or - wrong = "auther" and right = "author" - or - wrong = "authobiographic" and right = "autobiographic" - or - wrong = "authobiography" and right = "autobiography" - or - wrong = "authorative" and right = "authoritative" - or - wrong = "authorites" and right = "authorities" - or - wrong = "authorithy" and right = "authority" - or - wrong = "authoritiers" and right = "authorities" - or - wrong = "authoritive" and right = "authoritative" - or - wrong = "authrorities" and right = "authorities" - or - wrong = "autochtonous" and right = "autochthonous" - or - wrong = "autoctonous" and right = "autochthonous" - or - wrong = "automaticly" and right = "automatically" - or - wrong = "automibile" and right = "automobile" - or - wrong = "automonomous" and right = "autonomous" - or - wrong = "autor" and right = "author" - or - wrong = "autority" and right = "authority" - or - wrong = "auxilary" and right = "auxiliary" - or - wrong = "auxillaries" and right = "auxiliaries" - or - wrong = "auxillary" and right = "auxiliary" - or - wrong = "auxilliaries" and right = "auxiliaries" - or - wrong = "auxilliary" and right = "auxiliary" - or - wrong = "availabe" and right = "available" - or - wrong = "availablity" and right = "availability" - or - wrong = "availaible" and right = "available" - or - wrong = "availble" and right = "available" - or - wrong = "availiable" and right = "available" - or - wrong = "availible" and right = "available" - or - wrong = "avalable" and right = "available" - or - wrong = "avalance" and right = "avalanche" - or - wrong = "avaliable" and right = "available" - or - wrong = "avation" and right = "aviation" - or - wrong = "averageed" and right = "averaged" - or - wrong = "avilable" and right = "available" - or - wrong = "awared" and right = "awarded" - or - wrong = "awya" and right = "away" - or - wrong = "baceause" and right = "because" - or - wrong = "backgorund" and right = "background" - or - wrong = "backrounds" and right = "backgrounds" - or - wrong = "bakc" and right = "back" - or - wrong = "banannas" and right = "bananas" - or - wrong = "bandwith" and right = "bandwidth" - or - wrong = "bankrupcy" and right = "bankruptcy" - or - wrong = "banruptcy" and right = "bankruptcy" - or - wrong = "baout" and right = "about" - or - wrong = "baout" and right = "bout" - or - wrong = "basicaly" and right = "basically" - or - wrong = "basicly" and right = "basically" - or - wrong = "bcak" and right = "back" - or - wrong = "beachead" and right = "beachhead" - or - wrong = "beacuse" and right = "because" - or - wrong = "beastiality" and right = "bestiality" - or - wrong = "beatiful" and right = "beautiful" - or - wrong = "beaurocracy" and right = "bureaucracy" - or - wrong = "beaurocratic" and right = "bureaucratic" - or - wrong = "beautyfull" and right = "beautiful" - or - wrong = "becamae" and right = "became" - or - wrong = "becames" and right = "became" - or - wrong = "becames" and right = "becomes" - or - wrong = "becasue" and right = "because" - or - wrong = "beccause" and right = "because" - or - wrong = "becomeing" and right = "becoming" - or - wrong = "becomming" and right = "becoming" - or - wrong = "becouse" and right = "because" - or - wrong = "becuase" and right = "because" - or - wrong = "bedore" and right = "before" - or - wrong = "beeing" and right = "being" - or - wrong = "befoer" and right = "before" - or - wrong = "beggin" and right = "begging" - or - wrong = "beggin" and right = "begin" - or - wrong = "begginer" and right = "beginner" - or - wrong = "begginers" and right = "beginners" - or - wrong = "beggining" and right = "beginning" - or - wrong = "begginings" and right = "beginnings" - or - wrong = "beggins" and right = "begins" - or - wrong = "begining" and right = "beginning" - or - wrong = "beginnig" and right = "beginning" - or - wrong = "behavour" and right = "behavior" - or - wrong = "behavour" and right = "behaviour" - or - wrong = "beleagured" and right = "beleaguered" - or - wrong = "beleif" and right = "belief" - or - wrong = "beleive" and right = "believe" - or - wrong = "beleived" and right = "believed" - or - wrong = "beleives" and right = "believes" - or - wrong = "beleiving" and right = "believing" - or - wrong = "beligum" and right = "belgium" - or - wrong = "belive" and right = "believe" - or - wrong = "belived" and right = "believed" - or - wrong = "belived" and right = "beloved" - or - wrong = "belives" and right = "beliefs" - or - wrong = "belives" and right = "believes" - or - wrong = "belligerant" and right = "belligerent" - or - wrong = "bellweather" and right = "bellwether" - or - wrong = "bemusemnt" and right = "bemusement" - or - wrong = "beneficary" and right = "beneficiary" - or - wrong = "beng" and right = "being" - or - wrong = "benificial" and right = "beneficial" - or - wrong = "benifit" and right = "benefit" - or - wrong = "benifits" and right = "benefits" - or - wrong = "bergamont" and right = "bergamot" - or - wrong = "bernouilli" and right = "bernoulli" - or - wrong = "beseige" and right = "besiege" - or - wrong = "beseiged" and right = "besieged" - or - wrong = "beseiging" and right = "besieging" - or - wrong = "beteen" and right = "between" - or - wrong = "betwen" and right = "between" - or - wrong = "beween" and right = "between" - or - wrong = "bewteen" and right = "between" - or - wrong = "bigining" and right = "beginning" - or - wrong = "biginning" and right = "beginning" - or - wrong = "bilateraly" and right = "bilaterally" - or - wrong = "billingualism" and right = "bilingualism" - or - wrong = "binominal" and right = "binomial" - or - wrong = "bizzare" and right = "bizarre" - or - wrong = "blaim" and right = "blame" - or - wrong = "blaimed" and right = "blamed" - or - wrong = "blessure" and right = "blessing" - or - wrong = "blitzkreig" and right = "blitzkrieg" - or - wrong = "boaut" and right = "about" - or - wrong = "boaut" and right = "boat" - or - wrong = "boaut" and right = "bout" - or - wrong = "bodydbuilder" and right = "bodybuilder" - or - wrong = "bolean" and right = "boolean" - or - wrong = "bombardement" and right = "bombardment" - or - wrong = "bombarment" and right = "bombardment" - or - wrong = "bondary" and right = "boundary" - or - wrong = "bonnano" and right = "bonanno" - or - wrong = "boook" and right = "book" - or - wrong = "borke" and right = "broke" - or - wrong = "boundry" and right = "boundary" - or - wrong = "bouyancy" and right = "buoyancy" - or - wrong = "bouyant" and right = "buoyant" - or - wrong = "boyant" and right = "buoyant" - or - wrong = "bradcast" and right = "broadcast" - or - wrong = "brasillian" and right = "brazilian" - or - wrong = "breakthough" and right = "breakthrough" - or - wrong = "breakthroughts" and right = "breakthroughs" - or - wrong = "breif" and right = "brief" - or - wrong = "breifly" and right = "briefly" - or - wrong = "brethen" and right = "brethren" - or - wrong = "bretheren" and right = "brethren" - or - wrong = "briliant" and right = "brilliant" - or - wrong = "brillant" and right = "brilliant" - or - wrong = "brimestone" and right = "brimstone" - or - wrong = "britian" and right = "britain" - or - wrong = "brittish" and right = "british" - or - wrong = "broacasted" and right = "broadcast" - or - wrong = "broadacasting" and right = "broadcasting" - or - wrong = "broady" and right = "broadly" - or - wrong = "buddah" and right = "buddha" - or - wrong = "buddist" and right = "buddhist" - or - wrong = "buisness" and right = "business" - or - wrong = "buisnessman" and right = "businessman" - or - wrong = "buoancy" and right = "buoyancy" - or - wrong = "buring" and right = "burin" - or - wrong = "buring" and right = "burning" - or - wrong = "buring" and right = "burying" - or - wrong = "buring" and right = "during" - or - wrong = "burried" and right = "buried" - or - wrong = "busines" and right = "business" - or - wrong = "busineses" and right = "business" - or - wrong = "busineses" and right = "businesses" - or - wrong = "busness" and right = "business" - or - wrong = "bussiness" and right = "business" - or - wrong = "caculater" and right = "calculator" - or - wrong = "cacuses" and right = "caucuses" - or - wrong = "cahracters" and right = "characters" - or - wrong = "calaber" and right = "caliber" - or - wrong = "calander" and right = "calendar" - or - wrong = "calander" and right = "calender" - or - wrong = "calander" and right = "colander" - or - wrong = "calculater" and right = "calculator" - or - wrong = "calculs" and right = "calculus" - or - wrong = "calender" and right = "calendar" - or - wrong = "calenders" and right = "calendars" - or - wrong = "caligraphy" and right = "calligraphy" - or - wrong = "caluclate" and right = "calculate" - or - wrong = "caluclated" and right = "calculated" - or - wrong = "caluculate" and right = "calculate" - or - wrong = "caluculated" and right = "calculated" - or - wrong = "calulate" and right = "calculate" - or - wrong = "calulated" and right = "calculated" - or - wrong = "calulater" and right = "calculator" - or - wrong = "cambrige" and right = "cambridge" - or - wrong = "camoflage" and right = "camouflage" - or - wrong = "campagin" and right = "campaign" - or - wrong = "campain" and right = "campaign" - or - wrong = "campains" and right = "campaigns" - or - wrong = "candadate" and right = "candidate" - or - wrong = "candiate" and right = "candidate" - or - wrong = "candidiate" and right = "candidate" - or - wrong = "cannister" and right = "canister" - or - wrong = "cannisters" and right = "canisters" - or - wrong = "cannnot" and right = "cannot" - or - wrong = "cannonical" and right = "canonical" - or - wrong = "cannotation" and right = "connotation" - or - wrong = "cannotations" and right = "connotations" - or - wrong = "caost" and right = "coast" - or - wrong = "caperbility" and right = "capability" - or - wrong = "capible" and right = "capable" - or - wrong = "captial" and right = "capital" - or - wrong = "captued" and right = "captured" - or - wrong = "capturd" and right = "captured" - or - wrong = "carachter" and right = "character" - or - wrong = "caracterized" and right = "characterized" - or - wrong = "carcas" and right = "caracas" - or - wrong = "carcas" and right = "carcass" - or - wrong = "carefull" and right = "careful" - or - wrong = "careing" and right = "caring" - or - wrong = "carismatic" and right = "charismatic" - or - wrong = "carmalite" and right = "carmelite" - or - wrong = "carnagie" and right = "carnegie" - or - wrong = "carnege" and right = "carnage" - or - wrong = "carnege" and right = "carnegie" - or - wrong = "carnige" and right = "carnage" - or - wrong = "carnige" and right = "carnegie" - or - wrong = "carnigie" and right = "carnegie" - or - wrong = "carreer" and right = "career" - or - wrong = "carrers" and right = "careers" - or - wrong = "carribbean" and right = "caribbean" - or - wrong = "carribean" and right = "caribbean" - or - wrong = "carryng" and right = "carrying" - or - wrong = "cartdridge" and right = "cartridge" - or - wrong = "carthagian" and right = "carthaginian" - or - wrong = "carthographer" and right = "cartographer" - or - wrong = "cartilege" and right = "cartilage" - or - wrong = "cartilidge" and right = "cartilage" - or - wrong = "cartrige" and right = "cartridge" - or - wrong = "casette" and right = "cassette" - or - wrong = "casion" and right = "caisson" - or - wrong = "cassawory" and right = "cassowary" - or - wrong = "cassowarry" and right = "cassowary" - or - wrong = "casue" and right = "cause" - or - wrong = "casued" and right = "caused" - or - wrong = "casues" and right = "causes" - or - wrong = "casuing" and right = "causing" - or - wrong = "casulaties" and right = "casualties" - or - wrong = "casulaty" and right = "casualty" - or - wrong = "catagories" and right = "categories" - or - wrong = "catagorized" and right = "categorized" - or - wrong = "catagory" and right = "category" - or - wrong = "cataline" and right = "catalina" - or - wrong = "cataline" and right = "catiline" - or - wrong = "catapillar" and right = "caterpillar" - or - wrong = "catapillars" and right = "caterpillars" - or - wrong = "catapiller" and right = "caterpillar" - or - wrong = "catapillers" and right = "caterpillars" - or - wrong = "catepillar" and right = "caterpillar" - or - wrong = "catepillars" and right = "caterpillars" - or - wrong = "catergorize" and right = "categorize" - or - wrong = "catergorized" and right = "categorized" - or - wrong = "caterpilar" and right = "caterpillar" - or - wrong = "caterpilars" and right = "caterpillars" - or - wrong = "caterpiller" and right = "caterpillar" - or - wrong = "caterpillers" and right = "caterpillars" - or - wrong = "cathlic" and right = "catholic" - or - wrong = "catholocism" and right = "catholicism" - or - wrong = "catterpilar" and right = "caterpillar" - or - wrong = "catterpilars" and right = "caterpillars" - or - wrong = "catterpillar" and right = "caterpillar" - or - wrong = "catterpillars" and right = "caterpillars" - or - wrong = "cattleship" and right = "battleship" - or - wrong = "causalities" and right = "casualties" - or - wrong = "ceasar" and right = "caesar" - or - wrong = "celcius" and right = "celsius" - or - wrong = "cellpading" and right = "cellpadding" - or - wrong = "cementary" and right = "cemetery" - or - wrong = "cemetarey" and right = "cemetery" - or - wrong = "cemetaries" and right = "cemeteries" - or - wrong = "cemetary" and right = "cemetery" - or - wrong = "cencus" and right = "census" - or - wrong = "censur" and right = "censor" - or - wrong = "censur" and right = "censure" - or - wrong = "cententenial" and right = "centennial" - or - wrong = "centruies" and right = "centuries" - or - wrong = "centruy" and right = "century" - or - wrong = "centuties" and right = "centuries" - or - wrong = "centuty" and right = "century" - or - wrong = "ceratin" and right = "certain" - or - wrong = "ceratin" and right = "keratin" - or - wrong = "cerimonial" and right = "ceremonial" - or - wrong = "cerimonies" and right = "ceremonies" - or - wrong = "cerimonious" and right = "ceremonious" - or - wrong = "cerimony" and right = "ceremony" - or - wrong = "ceromony" and right = "ceremony" - or - wrong = "certainity" and right = "certainty" - or - wrong = "certian" and right = "certain" - or - wrong = "cervial" and right = "cervical" - or - wrong = "cervial" and right = "serval" - or - wrong = "cervial" and right = "servile" - or - wrong = "chalenging" and right = "challenging" - or - wrong = "challange" and right = "challenge" - or - wrong = "challanged" and right = "challenged" - or - wrong = "challege" and right = "challenge" - or - wrong = "champange" and right = "champagne" - or - wrong = "changable" and right = "changeable" - or - wrong = "charachter" and right = "character" - or - wrong = "charachters" and right = "characters" - or - wrong = "charactersistic" and right = "characteristic" - or - wrong = "charactor" and right = "character" - or - wrong = "charactors" and right = "characters" - or - wrong = "charasmatic" and right = "charismatic" - or - wrong = "charaterized" and right = "characterized" - or - wrong = "chariman" and right = "chairman" - or - wrong = "charistics" and right = "characteristics" - or - wrong = "chasr" and right = "chase" - or - wrong = "chasr" and right = "chaser" - or - wrong = "cheif" and right = "chief" - or - wrong = "cheifs" and right = "chiefs" - or - wrong = "chemcial" and right = "chemical" - or - wrong = "chemcially" and right = "chemically" - or - wrong = "chemestry" and right = "chemistry" - or - wrong = "chemicaly" and right = "chemically" - or - wrong = "childbird" and right = "childbirth" - or - wrong = "childen" and right = "children" - or - wrong = "choclate" and right = "chocolate" - or - wrong = "choosen" and right = "chosen" - or - wrong = "chracter" and right = "character" - or - wrong = "chuch" and right = "church" - or - wrong = "churchs" and right = "churches" - or - wrong = "cincinatti" and right = "cincinnati" - or - wrong = "cincinnatti" and right = "cincinnati" - or - wrong = "circulaton" and right = "circulation" - or - wrong = "circumsicion" and right = "circumcision" - or - wrong = "circut" and right = "circuit" - or - wrong = "ciricuit" and right = "circuit" - or - wrong = "ciriculum" and right = "curriculum" - or - wrong = "civillian" and right = "civilian" - or - wrong = "claer" and right = "clear" - or - wrong = "claerer" and right = "clearer" - or - wrong = "claerly" and right = "clearly" - or - wrong = "claimes" and right = "claims" - or - wrong = "clas" and right = "class" - or - wrong = "clasic" and right = "classic" - or - wrong = "clasical" and right = "classical" - or - wrong = "clasically" and right = "classically" - or - wrong = "cleareance" and right = "clearance" - or - wrong = "clera" and right = "clear" - or - wrong = "clera" and right = "sclera" - or - wrong = "clincial" and right = "clinical" - or - wrong = "clinicaly" and right = "clinically" - or - wrong = "cmo" and right = "com" - or - wrong = "cmoputer" and right = "computer" - or - wrong = "coctail" and right = "cocktail" - or - wrong = "coform" and right = "conform" - or - wrong = "cognizent" and right = "cognizant" - or - wrong = "coincedentally" and right = "coincidentally" - or - wrong = "colaborations" and right = "collaborations" - or - wrong = "colateral" and right = "collateral" - or - wrong = "colelctive" and right = "collective" - or - wrong = "collaberative" and right = "collaborative" - or - wrong = "collecton" and right = "collection" - or - wrong = "collegue" and right = "colleague" - or - wrong = "collegues" and right = "colleagues" - or - wrong = "collonade" and right = "colonnade" - or - wrong = "collonies" and right = "colonies" - or - wrong = "collony" and right = "colony" - or - wrong = "collosal" and right = "colossal" - or - wrong = "colonizators" and right = "colonizers" - or - wrong = "comander" and right = "commandeer" - or - wrong = "comander" and right = "commander" - or - wrong = "comando" and right = "commando" - or - wrong = "comandos" and right = "commandos" - or - wrong = "comany" and right = "company" - or - wrong = "comapany" and right = "company" - or - wrong = "comback" and right = "comeback" - or - wrong = "combanations" and right = "combinations" - or - wrong = "combinatins" and right = "combinations" - or - wrong = "combusion" and right = "combustion" - or - wrong = "comdemnation" and right = "condemnation" - or - wrong = "comemmorates" and right = "commemorates" - or - wrong = "comemoretion" and right = "commemoration" - or - wrong = "comision" and right = "commission" - or - wrong = "comisioned" and right = "commissioned" - or - wrong = "comisioner" and right = "commissioner" - or - wrong = "comisioning" and right = "commissioning" - or - wrong = "comisions" and right = "commissions" - or - wrong = "comission" and right = "commission" - or - wrong = "comissioned" and right = "commissioned" - or - wrong = "comissioner" and right = "commissioner" - or - wrong = "comissioning" and right = "commissioning" - or - wrong = "comissions" and right = "commissions" - or - wrong = "comited" and right = "committed" - or - wrong = "comiting" and right = "committing" - or - wrong = "comitted" and right = "committed" - or - wrong = "comittee" and right = "committee" - or - wrong = "comitting" and right = "committing" - or - wrong = "commandoes" and right = "commandos" - or - wrong = "commedic" and right = "comedic" - or - wrong = "commemerative" and right = "commemorative" - or - wrong = "commemmorate" and right = "commemorate" - or - wrong = "commemmorating" and right = "commemorating" - or - wrong = "commerical" and right = "commercial" - or - wrong = "commerically" and right = "commercially" - or - wrong = "commericial" and right = "commercial" - or - wrong = "commericially" and right = "commercially" - or - wrong = "commerorative" and right = "commemorative" - or - wrong = "comming" and right = "coming" - or - wrong = "comminication" and right = "communication" - or - wrong = "commision" and right = "commission" - or - wrong = "commisioned" and right = "commissioned" - or - wrong = "commisioner" and right = "commissioner" - or - wrong = "commisioning" and right = "commissioning" - or - wrong = "commisions" and right = "commissions" - or - wrong = "commited" and right = "committed" - or - wrong = "commitee" and right = "committee" - or - wrong = "commiting" and right = "committing" - or - wrong = "committe" and right = "committee" - or - wrong = "committment" and right = "commitment" - or - wrong = "committments" and right = "commitments" - or - wrong = "commmemorated" and right = "commemorated" - or - wrong = "commongly" and right = "commonly" - or - wrong = "commonweath" and right = "commonwealth" - or - wrong = "commuications" and right = "communications" - or - wrong = "commuinications" and right = "communications" - or - wrong = "communciation" and right = "communication" - or - wrong = "communiation" and right = "communication" - or - wrong = "communites" and right = "communities" - or - wrong = "compability" and right = "compatibility" - or - wrong = "comparision" and right = "comparison" - or - wrong = "comparisions" and right = "comparisons" - or - wrong = "comparitive" and right = "comparative" - or - wrong = "comparitively" and right = "comparatively" - or - wrong = "compatabilities" and right = "compatibilities" - or - wrong = "compatability" and right = "compatibility" - or - wrong = "compatable" and right = "compatible" - or - wrong = "compatablities" and right = "compatibilities" - or - wrong = "compatablity" and right = "compatibility" - or - wrong = "compatiable" and right = "compatible" - or - wrong = "compatiblities" and right = "compatibilities" - or - wrong = "compatiblity" and right = "compatibility" - or - wrong = "compeitions" and right = "competitions" - or - wrong = "compensantion" and right = "compensation" - or - wrong = "competance" and right = "competence" - or - wrong = "competant" and right = "competent" - or - wrong = "competative" and right = "competitive" - or - wrong = "competion" and right = "competition" - or - wrong = "competion" and right = "completion" - or - wrong = "competitiion" and right = "competition" - or - wrong = "competive" and right = "competitive" - or - wrong = "competiveness" and right = "competitiveness" - or - wrong = "comphrehensive" and right = "comprehensive" - or - wrong = "compitent" and right = "competent" - or - wrong = "completelyl" and right = "completely" - or - wrong = "completetion" and right = "completion" - or - wrong = "complier" and right = "compiler" - or - wrong = "componant" and right = "component" - or - wrong = "comprable" and right = "comparable" - or - wrong = "comprimise" and right = "compromise" - or - wrong = "compulsary" and right = "compulsory" - or - wrong = "compulsery" and right = "compulsory" - or - wrong = "computarized" and right = "computerized" - or - wrong = "concensus" and right = "consensus" - or - wrong = "concider" and right = "consider" - or - wrong = "concidered" and right = "considered" - or - wrong = "concidering" and right = "considering" - or - wrong = "conciders" and right = "considers" - or - wrong = "concieted" and right = "conceited" - or - wrong = "concieved" and right = "conceived" - or - wrong = "concious" and right = "conscious" - or - wrong = "conciously" and right = "consciously" - or - wrong = "conciousness" and right = "consciousness" - or - wrong = "condamned" and right = "condemned" - or - wrong = "condemmed" and right = "condemned" - or - wrong = "condidtion" and right = "condition" - or - wrong = "condidtions" and right = "conditions" - or - wrong = "conected" and right = "connected" - or - wrong = "conection" and right = "connection" - or - wrong = "conesencus" and right = "consensus" - or - wrong = "confidental" and right = "confidential" - or - wrong = "confidentally" and right = "confidentially" - or - wrong = "confids" and right = "confides" - or - wrong = "configureable" and right = "configurable" - or - wrong = "confortable" and right = "comfortable" - or - wrong = "congradulations" and right = "congratulations" - or - wrong = "congresional" and right = "congressional" - or - wrong = "conived" and right = "connived" - or - wrong = "conjecutre" and right = "conjecture" - or - wrong = "conjuction" and right = "conjunction" - or - wrong = "connectinos" and right = "connections" - or - wrong = "conneticut" and right = "connecticut" - or - wrong = "conotations" and right = "connotations" - or - wrong = "conquerd" and right = "conquered" - or - wrong = "conquerer" and right = "conqueror" - or - wrong = "conquerers" and right = "conquerors" - or - wrong = "conqured" and right = "conquered" - or - wrong = "conscent" and right = "consent" - or - wrong = "consciouness" and right = "consciousness" - or - wrong = "consdider" and right = "consider" - or - wrong = "consdidered" and right = "considered" - or - wrong = "consdiered" and right = "considered" - or - wrong = "consectutive" and right = "consecutive" - or - wrong = "consenquently" and right = "consequently" - or - wrong = "consentrate" and right = "concentrate" - or - wrong = "consentrated" and right = "concentrated" - or - wrong = "consentrates" and right = "concentrates" - or - wrong = "consept" and right = "concept" - or - wrong = "consequentually" and right = "consequently" - or - wrong = "consequeseces" and right = "consequences" - or - wrong = "consern" and right = "concern" - or - wrong = "conserned" and right = "concerned" - or - wrong = "conserning" and right = "concerning" - or - wrong = "conservitive" and right = "conservative" - or - wrong = "consiciousness" and right = "consciousness" - or - wrong = "consicousness" and right = "consciousness" - or - wrong = "considerd" and right = "considered" - or - wrong = "consideres" and right = "considered" - or - wrong = "consious" and right = "conscious" - or - wrong = "consistant" and right = "consistent" - or - wrong = "consistantly" and right = "consistently" - or - wrong = "consituencies" and right = "constituencies" - or - wrong = "consituency" and right = "constituency" - or - wrong = "consituted" and right = "constituted" - or - wrong = "consitution" and right = "constitution" - or - wrong = "consitutional" and right = "constitutional" - or - wrong = "consolodate" and right = "consolidate" - or - wrong = "consolodated" and right = "consolidated" - or - wrong = "consonent" and right = "consonant" - or - wrong = "consonents" and right = "consonants" - or - wrong = "consorcium" and right = "consortium" - or - wrong = "conspiracys" and right = "conspiracies" - or - wrong = "conspiriator" and right = "conspirator" - or - wrong = "consructor" and right = "constructor" - or - wrong = "constaints" and right = "constraints" - or - wrong = "constanly" and right = "constantly" - or - wrong = "constarnation" and right = "consternation" - or - wrong = "constatn" and right = "constant" - or - wrong = "constinually" and right = "continually" - or - wrong = "constituant" and right = "constituent" - or - wrong = "constituants" and right = "constituents" - or - wrong = "constituion" and right = "constitution" - or - wrong = "constituional" and right = "constitutional" - or - wrong = "consttruction" and right = "construction" - or - wrong = "constuction" and right = "construction" - or - wrong = "consulant" and right = "consultant" - or - wrong = "consumate" and right = "consummate" - or - wrong = "consumated" and right = "consummated" - or - wrong = "contaiminate" and right = "contaminate" - or - wrong = "containes" and right = "contains" - or - wrong = "contamporaries" and right = "contemporaries" - or - wrong = "contamporary" and right = "contemporary" - or - wrong = "contempoary" and right = "contemporary" - or - wrong = "contemporaneus" and right = "contemporaneous" - or - wrong = "contempory" and right = "contemporary" - or - wrong = "contendor" and right = "contender" - or - wrong = "contian" and right = "contain" - or - wrong = "contians" and right = "contains" - or - wrong = "contibute" and right = "contribute" - or - wrong = "contibuted" and right = "contributed" - or - wrong = "contibutes" and right = "contributes" - or - wrong = "contigent" and right = "contingent" - or - wrong = "contined" and right = "continued" - or - wrong = "continential" and right = "continental" - or - wrong = "continous" and right = "continuous" - or - wrong = "continously" and right = "continuously" - or - wrong = "continueing" and right = "continuing" - or - wrong = "contravercial" and right = "controversial" - or - wrong = "contraversy" and right = "controversy" - or - wrong = "contributer" and right = "contributor" - or - wrong = "contributers" and right = "contributors" - or - wrong = "contritutions" and right = "contributions" - or - wrong = "controled" and right = "controlled" - or - wrong = "controling" and right = "controlling" - or - wrong = "controll" and right = "control" - or - wrong = "controlls" and right = "controls" - or - wrong = "controvercial" and right = "controversial" - or - wrong = "controvercy" and right = "controversy" - or - wrong = "controveries" and right = "controversies" - or - wrong = "controversal" and right = "controversial" - or - wrong = "controversey" and right = "controversy" - or - wrong = "controvertial" and right = "controversial" - or - wrong = "controvery" and right = "controversy" - or - wrong = "contruction" and right = "construction" - or - wrong = "contructor" and right = "constructor" - or - wrong = "contstruction" and right = "construction" - or - wrong = "conveinent" and right = "convenient" - or - wrong = "convenant" and right = "covenant" - or - wrong = "convential" and right = "conventional" - or - wrong = "convertables" and right = "convertibles" - or - wrong = "convertion" and right = "conversion" - or - wrong = "conviced" and right = "convinced" - or - wrong = "convienient" and right = "convenient" - or - wrong = "coordiantion" and right = "coordination" - or - wrong = "coorperation" and right = "cooperation" - or - wrong = "coorperation" and right = "corporation" - or - wrong = "coorperations" and right = "corporations" - or - wrong = "copmetitors" and right = "competitors" - or - wrong = "coputer" and right = "computer" - or - wrong = "copywrite" and right = "copyright" - or - wrong = "coridal" and right = "cordial" - or - wrong = "cornmitted" and right = "committed" - or - wrong = "corosion" and right = "corrosion" - or - wrong = "corparate" and right = "corporate" - or - wrong = "corperations" and right = "corporations" - or - wrong = "correcters" and right = "correctors" - or - wrong = "correponding" and right = "corresponding" - or - wrong = "correposding" and right = "corresponding" - or - wrong = "correspondant" and right = "correspondent" - or - wrong = "correspondants" and right = "correspondents" - or - wrong = "corridoors" and right = "corridors" - or - wrong = "corrispond" and right = "correspond" - or - wrong = "corrispondant" and right = "correspondent" - or - wrong = "corrispondants" and right = "correspondents" - or - wrong = "corrisponded" and right = "corresponded" - or - wrong = "corrisponding" and right = "corresponding" - or - wrong = "corrisponds" and right = "corresponds" - or - wrong = "costitution" and right = "constitution" - or - wrong = "coucil" and right = "council" - or - wrong = "coudl" and right = "cloud" - or - wrong = "coudl" and right = "could" - or - wrong = "councellor" and right = "councillor" - or - wrong = "councellor" and right = "councilor" - or - wrong = "councellor" and right = "counselor" - or - wrong = "councellors" and right = "councillors" - or - wrong = "councellors" and right = "councilors" - or - wrong = "councellors" and right = "counselors" - or - wrong = "counries" and right = "countries" - or - wrong = "countains" and right = "contains" - or - wrong = "countires" and right = "countries" - or - wrong = "coururier" and right = "courier" - or - wrong = "coururier" and right = "couturier" - or - wrong = "coverted" and right = "converted" - or - wrong = "coverted" and right = "covered" - or - wrong = "coverted" and right = "coveted" - or - wrong = "cpoy" and right = "copy" - or - wrong = "cpoy" and right = "coy" - or - wrong = "creaeted" and right = "created" - or - wrong = "creedence" and right = "credence" - or - wrong = "critereon" and right = "criterion" - or - wrong = "criterias" and right = "criteria" - or - wrong = "criticists" and right = "critics" - or - wrong = "critising" and right = "criticising" - or - wrong = "critising" and right = "criticizing" - or - wrong = "critisising" and right = "criticising" - or - wrong = "critisism" and right = "criticism" - or - wrong = "critisisms" and right = "criticisms" - or - wrong = "critisize" and right = "criticise" - or - wrong = "critisize" and right = "criticize" - or - wrong = "critisized" and right = "criticised" - or - wrong = "critisized" and right = "criticized" - or - wrong = "critisizes" and right = "criticises" - or - wrong = "critisizes" and right = "criticizes" - or - wrong = "critisizing" and right = "criticising" - or - wrong = "critisizing" and right = "criticizing" - or - wrong = "critized" and right = "criticized" - or - wrong = "critizing" and right = "criticizing" - or - wrong = "crockodiles" and right = "crocodiles" - or - wrong = "crowm" and right = "crown" - or - wrong = "crtical" and right = "critical" - or - wrong = "crticised" and right = "criticised" - or - wrong = "crucifiction" and right = "crucifixion" - or - wrong = "crusies" and right = "cruises" - or - wrong = "crutial" and right = "crucial" - or - wrong = "crystalisation" and right = "crystallisation" - or - wrong = "culiminating" and right = "culminating" - or - wrong = "cumulatative" and right = "cumulative" - or - wrong = "curch" and right = "church" - or - wrong = "curcuit" and right = "circuit" - or - wrong = "currenly" and right = "currently" - or - wrong = "curriculem" and right = "curriculum" - or - wrong = "cxan" and right = "cyan" - or - wrong = "cyclinder" and right = "cylinder" - or - wrong = "dacquiri" and right = "daiquiri" - or - wrong = "daed" and right = "dead" - or - wrong = "dael" and right = "dahl" - or - wrong = "dael" and right = "deal" - or - wrong = "dael" and right = "dial" - or - wrong = "dalmation" and right = "dalmatian" - or - wrong = "damenor" and right = "demeanor" - or - wrong = "dammage" and right = "damage" - or - wrong = "dardenelles" and right = "dardanelles" - or - wrong = "daugher" and right = "daughter" - or - wrong = "deafult" and right = "default" - or - wrong = "debateable" and right = "debatable" - or - wrong = "decendant" and right = "descendant" - or - wrong = "decendants" and right = "descendants" - or - wrong = "decendent" and right = "descendant" - or - wrong = "decendents" and right = "descendants" - or - wrong = "decideable" and right = "decidable" - or - wrong = "decidely" and right = "decidedly" - or - wrong = "decieved" and right = "deceived" - or - wrong = "decison" and right = "decision" - or - wrong = "decomissioned" and right = "decommissioned" - or - wrong = "decomposit" and right = "decompose" - or - wrong = "decomposited" and right = "decomposed" - or - wrong = "decompositing" and right = "decomposing" - or - wrong = "decomposits" and right = "decomposes" - or - wrong = "decress" and right = "decrees" - or - wrong = "decribe" and right = "describe" - or - wrong = "decribed" and right = "described" - or - wrong = "decribes" and right = "describes" - or - wrong = "decribing" and right = "describing" - or - wrong = "dectect" and right = "detect" - or - wrong = "defendent" and right = "defendant" - or - wrong = "defendents" and right = "defendants" - or - wrong = "deffensively" and right = "defensively" - or - wrong = "deffine" and right = "define" - or - wrong = "deffined" and right = "defined" - or - wrong = "definance" and right = "defiance" - or - wrong = "definate" and right = "definite" - or - wrong = "definately" and right = "definitely" - or - wrong = "definatly" and right = "definitely" - or - wrong = "definetly" and right = "definitely" - or - wrong = "definining" and right = "defining" - or - wrong = "definit" and right = "definite" - or - wrong = "definitly" and right = "definitely" - or - wrong = "definiton" and right = "definition" - or - wrong = "defintion" and right = "definition" - or - wrong = "defualt" and right = "default" - or - wrong = "defult" and right = "default" - or - wrong = "degrate" and right = "degrade" - or - wrong = "delagates" and right = "delegates" - or - wrong = "delapidated" and right = "dilapidated" - or - wrong = "delerious" and right = "delirious" - or - wrong = "delevopment" and right = "development" - or - wrong = "deliberatly" and right = "deliberately" - or - wrong = "delusionally" and right = "delusively" - or - wrong = "demenor" and right = "demeanor" - or - wrong = "demographical" and right = "demographic" - or - wrong = "demolision" and right = "demolition" - or - wrong = "demorcracy" and right = "democracy" - or - wrong = "demostration" and right = "demonstration" - or - wrong = "denegrating" and right = "denigrating" - or - wrong = "densly" and right = "densely" - or - wrong = "deparment" and right = "department" - or - wrong = "deparmental" and right = "departmental" - or - wrong = "deparments" and right = "departments" - or - wrong = "dependance" and right = "dependence" - or - wrong = "dependancy" and right = "dependency" - or - wrong = "deram" and right = "dram" - or - wrong = "deram" and right = "dream" - or - wrong = "deriviated" and right = "derived" - or - wrong = "derivitive" and right = "derivative" - or - wrong = "derogitory" and right = "derogatory" - or - wrong = "descendands" and right = "descendants" - or - wrong = "descibed" and right = "described" - or - wrong = "desciptors" and right = "descriptors" - or - wrong = "descision" and right = "decision" - or - wrong = "descisions" and right = "decisions" - or - wrong = "descriibes" and right = "describes" - or - wrong = "descripters" and right = "descriptors" - or - wrong = "descripton" and right = "description" - or - wrong = "desctruction" and right = "destruction" - or - wrong = "descuss" and right = "discuss" - or - wrong = "desgined" and right = "designed" - or - wrong = "deside" and right = "decide" - or - wrong = "desigining" and right = "designing" - or - wrong = "desinations" and right = "destinations" - or - wrong = "desintegrated" and right = "disintegrated" - or - wrong = "desintegration" and right = "disintegration" - or - wrong = "desireable" and right = "desirable" - or - wrong = "desitned" and right = "destined" - or - wrong = "desktiop" and right = "desktop" - or - wrong = "desorder" and right = "disorder" - or - wrong = "desoriented" and right = "disoriented" - or - wrong = "desparate" and right = "desperate" - or - wrong = "desparate" and right = "disparate" - or - wrong = "despict" and right = "depict" - or - wrong = "despiration" and right = "desperation" - or - wrong = "dessicated" and right = "desiccated" - or - wrong = "dessigned" and right = "designed" - or - wrong = "destablized" and right = "destabilized" - or - wrong = "destory" and right = "destroy" - or - wrong = "desugered" and right = "desugared" - or - wrong = "detailled" and right = "detailed" - or - wrong = "detatched" and right = "detached" - or - wrong = "deteoriated" and right = "deteriorated" - or - wrong = "deteriate" and right = "deteriorate" - or - wrong = "deterioriating" and right = "deteriorating" - or - wrong = "determinining" and right = "determining" - or - wrong = "detremental" and right = "detrimental" - or - wrong = "devasted" and right = "devastated" - or - wrong = "develope" and right = "develop" - or - wrong = "developement" and right = "development" - or - wrong = "developped" and right = "developed" - or - wrong = "develpment" and right = "development" - or - wrong = "devels" and right = "delves" - or - wrong = "devestated" and right = "devastated" - or - wrong = "devestating" and right = "devastating" - or - wrong = "devide" and right = "divide" - or - wrong = "devided" and right = "divided" - or - wrong = "devistating" and right = "devastating" - or - wrong = "devolopement" and right = "development" - or - wrong = "diablical" and right = "diabolical" - or - wrong = "diamons" and right = "diamonds" - or - wrong = "diaster" and right = "disaster" - or - wrong = "dichtomy" and right = "dichotomy" - or - wrong = "diconnects" and right = "disconnects" - or - wrong = "dicover" and right = "discover" - or - wrong = "dicovered" and right = "discovered" - or - wrong = "dicovering" and right = "discovering" - or - wrong = "dicovers" and right = "discovers" - or - wrong = "dicovery" and right = "discovery" - or - wrong = "dictionarys" and right = "dictionaries" - or - wrong = "dicussed" and right = "discussed" - or - wrong = "diea" and right = "die" - or - wrong = "diea" and right = "idea" - or - wrong = "dieing" and right = "dyeing" - or - wrong = "dieing" and right = "dying" - or - wrong = "dieties" and right = "deities" - or - wrong = "diety" and right = "deity" - or - wrong = "diferent" and right = "different" - or - wrong = "diferrent" and right = "different" - or - wrong = "differentiatiations" and right = "differentiations" - or - wrong = "differnt" and right = "different" - or - wrong = "difficulity" and right = "difficulty" - or - wrong = "diffrent" and right = "different" - or - wrong = "dificulties" and right = "difficulties" - or - wrong = "dificulty" and right = "difficulty" - or - wrong = "dimenions" and right = "dimensions" - or - wrong = "dimention" and right = "dimension" - or - wrong = "dimentional" and right = "dimensional" - or - wrong = "dimentions" and right = "dimensions" - or - wrong = "dimesnional" and right = "dimensional" - or - wrong = "diminuitive" and right = "diminutive" - or - wrong = "dimunitive" and right = "diminutive" - or - wrong = "diosese" and right = "diocese" - or - wrong = "diphtong" and right = "diphthong" - or - wrong = "diphtongs" and right = "diphthongs" - or - wrong = "diplomancy" and right = "diplomacy" - or - wrong = "dipthong" and right = "diphthong" - or - wrong = "dipthongs" and right = "diphthongs" - or - wrong = "directoty" and right = "directory" - or - wrong = "dirived" and right = "derived" - or - wrong = "disagreeed" and right = "disagreed" - or - wrong = "disapeared" and right = "disappeared" - or - wrong = "disapointing" and right = "disappointing" - or - wrong = "disappearred" and right = "disappeared" - or - wrong = "disaproval" and right = "disapproval" - or - wrong = "disasterous" and right = "disastrous" - or - wrong = "disatisfaction" and right = "dissatisfaction" - or - wrong = "disatisfied" and right = "dissatisfied" - or - wrong = "disatrous" and right = "disastrous" - or - wrong = "discontentment" and right = "discontent" - or - wrong = "discribe" and right = "describe" - or - wrong = "discribed" and right = "described" - or - wrong = "discribes" and right = "describes" - or - wrong = "discribing" and right = "describing" - or - wrong = "disctinction" and right = "distinction" - or - wrong = "disctinctive" and right = "distinctive" - or - wrong = "disemination" and right = "dissemination" - or - wrong = "disenchanged" and right = "disenchanted" - or - wrong = "disiplined" and right = "disciplined" - or - wrong = "disobediance" and right = "disobedience" - or - wrong = "disobediant" and right = "disobedient" - or - wrong = "disolved" and right = "dissolved" - or - wrong = "disover" and right = "discover" - or - wrong = "dispair" and right = "despair" - or - wrong = "disparingly" and right = "disparagingly" - or - wrong = "dispence" and right = "dispense" - or - wrong = "dispenced" and right = "dispensed" - or - wrong = "dispencing" and right = "dispensing" - or - wrong = "dispicable" and right = "despicable" - or - wrong = "dispite" and right = "despite" - or - wrong = "dispostion" and right = "disposition" - or - wrong = "disproportiate" and right = "disproportionate" - or - wrong = "disputandem" and right = "disputandum" - or - wrong = "disricts" and right = "districts" - or - wrong = "dissagreement" and right = "disagreement" - or - wrong = "dissapear" and right = "disappear" - or - wrong = "dissapearance" and right = "disappearance" - or - wrong = "dissapeared" and right = "disappeared" - or - wrong = "dissapearing" and right = "disappearing" - or - wrong = "dissapears" and right = "disappears" - or - wrong = "dissappear" and right = "disappear" - or - wrong = "dissappears" and right = "disappears" - or - wrong = "dissappointed" and right = "disappointed" - or - wrong = "dissarray" and right = "disarray" - or - wrong = "dissobediance" and right = "disobedience" - or - wrong = "dissobediant" and right = "disobedient" - or - wrong = "dissobedience" and right = "disobedience" - or - wrong = "dissobedient" and right = "disobedient" - or - wrong = "distiction" and right = "distinction" - or - wrong = "distingish" and right = "distinguish" - or - wrong = "distingished" and right = "distinguished" - or - wrong = "distingishes" and right = "distinguishes" - or - wrong = "distingishing" and right = "distinguishing" - or - wrong = "distingquished" and right = "distinguished" - or - wrong = "distrubution" and right = "distribution" - or - wrong = "distruction" and right = "destruction" - or - wrong = "distructive" and right = "destructive" - or - wrong = "ditributed" and right = "distributed" - or - wrong = "diversed" and right = "diverged" - or - wrong = "diversed" and right = "diverse" - or - wrong = "divice" and right = "device" - or - wrong = "divinition" and right = "divination" - or - wrong = "divison" and right = "division" - or - wrong = "divisons" and right = "divisions" - or - wrong = "doccument" and right = "document" - or - wrong = "doccumented" and right = "documented" - or - wrong = "doccuments" and right = "documents" - or - wrong = "docrines" and right = "doctrines" - or - wrong = "doctines" and right = "doctrines" - or - wrong = "documenatry" and right = "documentary" - or - wrong = "doens" and right = "does" - or - wrong = "doign" and right = "doing" - or - wrong = "dominaton" and right = "domination" - or - wrong = "dominent" and right = "dominant" - or - wrong = "dominiant" and right = "dominant" - or - wrong = "donig" and right = "doing" - or - wrong = "doub" and right = "daub" - or - wrong = "doub" and right = "doubt" - or - wrong = "doulbe" and right = "double" - or - wrong = "dowloads" and right = "downloads" - or - wrong = "dramtic" and right = "dramatic" - or - wrong = "draughtman" and right = "draughtsman" - or - wrong = "dravadian" and right = "dravidian" - or - wrong = "dreasm" and right = "dreams" - or - wrong = "driectly" and right = "directly" - or - wrong = "drnik" and right = "drink" - or - wrong = "druming" and right = "drumming" - or - wrong = "drummless" and right = "drumless" - or - wrong = "dum" and right = "dumb" - or - wrong = "dupicate" and right = "duplicate" - or - wrong = "durig" and right = "during" - or - wrong = "durring" and right = "during" - or - wrong = "duting" and right = "during" - or - wrong = "dyas" and right = "dryas" - or - wrong = "eahc" and right = "each" - or - wrong = "ealier" and right = "earlier" - or - wrong = "earlies" and right = "earliest" - or - wrong = "earnt" and right = "earned" - or - wrong = "ecclectic" and right = "eclectic" - or - wrong = "eceonomy" and right = "economy" - or - wrong = "ecidious" and right = "deciduous" - or - wrong = "eclispe" and right = "eclipse" - or - wrong = "ecomonic" and right = "economic" - or - wrong = "ect" and right = "etc" - or - wrong = "editting" and right = "editing" - or - wrong = "eearly" and right = "early" - or - wrong = "efel" and right = "evil" - or - wrong = "effeciency" and right = "efficiency" - or - wrong = "effecient" and right = "efficient" - or - wrong = "effeciently" and right = "efficiently" - or - wrong = "efficency" and right = "efficiency" - or - wrong = "efficent" and right = "efficient" - or - wrong = "efficently" and right = "efficiently" - or - wrong = "efford" and right = "afford" - or - wrong = "efford" and right = "effort" - or - wrong = "effords" and right = "affords" - or - wrong = "effords" and right = "efforts" - or - wrong = "effulence" and right = "effluence" - or - wrong = "eigth" and right = "eight" - or - wrong = "eigth" and right = "eighth" - or - wrong = "eiter" and right = "either" - or - wrong = "elction" and right = "election" - or - wrong = "electic" and right = "eclectic" - or - wrong = "electic" and right = "electric" - or - wrong = "electon" and right = "election" - or - wrong = "electon" and right = "electron" - or - wrong = "electrial" and right = "electrical" - or - wrong = "electricly" and right = "electrically" - or - wrong = "electricty" and right = "electricity" - or - wrong = "elementay" and right = "elementary" - or - wrong = "eleminated" and right = "eliminated" - or - wrong = "eleminating" and right = "eliminating" - or - wrong = "eles" and right = "eels" - or - wrong = "eletricity" and right = "electricity" - or - wrong = "elicided" and right = "elicited" - or - wrong = "eligable" and right = "eligible" - or - wrong = "elimentary" and right = "elementary" - or - wrong = "ellected" and right = "elected" - or - wrong = "elphant" and right = "elephant" - or - wrong = "embarass" and right = "embarrass" - or - wrong = "embarassed" and right = "embarrassed" - or - wrong = "embarassing" and right = "embarrassing" - or - wrong = "embarassment" and right = "embarrassment" - or - wrong = "embargos" and right = "embargoes" - or - wrong = "embarras" and right = "embarrass" - or - wrong = "embarrased" and right = "embarrassed" - or - wrong = "embarrasing" and right = "embarrassing" - or - wrong = "embarrasment" and right = "embarrassment" - or - wrong = "embezelled" and right = "embezzled" - or - wrong = "emblamatic" and right = "emblematic" - or - wrong = "eminate" and right = "emanate" - or - wrong = "eminated" and right = "emanated" - or - wrong = "emision" and right = "emission" - or - wrong = "emited" and right = "emitted" - or - wrong = "emiting" and right = "emitting" - or - wrong = "emition" and right = "emission" - or - wrong = "emition" and right = "emotion" - or - wrong = "emmediately" and right = "immediately" - or - wrong = "emmigrated" and right = "emigrated" - or - wrong = "emmigrated" and right = "immigrated" - or - wrong = "emminent" and right = "eminent" - or - wrong = "emminent" and right = "imminent" - or - wrong = "emminently" and right = "eminently" - or - wrong = "emmisaries" and right = "emissaries" - or - wrong = "emmisarries" and right = "emissaries" - or - wrong = "emmisarry" and right = "emissary" - or - wrong = "emmisary" and right = "emissary" - or - wrong = "emmision" and right = "emission" - or - wrong = "emmisions" and right = "emissions" - or - wrong = "emmited" and right = "emitted" - or - wrong = "emmiting" and right = "emitting" - or - wrong = "emmitted" and right = "emitted" - or - wrong = "emmitting" and right = "emitting" - or - wrong = "emnity" and right = "enmity" - or - wrong = "emperical" and right = "empirical" - or - wrong = "emphaised" and right = "emphasised" - or - wrong = "emphsis" and right = "emphasis" - or - wrong = "emphysyma" and right = "emphysema" - or - wrong = "empirial" and right = "empirical" - or - wrong = "empirial" and right = "imperial" - or - wrong = "emporer" and right = "emperor" - or - wrong = "emprisoned" and right = "imprisoned" - or - wrong = "enameld" and right = "enameled" - or - wrong = "enchancement" and right = "enhancement" - or - wrong = "encouraing" and right = "encouraging" - or - wrong = "encryptiion" and right = "encryption" - or - wrong = "encylopedia" and right = "encyclopedia" - or - wrong = "endevors" and right = "endeavors" - or - wrong = "endevour" and right = "endeavour" - or - wrong = "endig" and right = "ending" - or - wrong = "endolithes" and right = "endoliths" - or - wrong = "enduce" and right = "induce" - or - wrong = "ened" and right = "need" - or - wrong = "enforceing" and right = "enforcing" - or - wrong = "engagment" and right = "engagement" - or - wrong = "engeneer" and right = "engineer" - or - wrong = "engeneering" and right = "engineering" - or - wrong = "engieneer" and right = "engineer" - or - wrong = "engieneers" and right = "engineers" - or - wrong = "enlargment" and right = "enlargement" - or - wrong = "enlargments" and right = "enlargements" - or - wrong = "enlish" and right = "english" - or - wrong = "enlish" and right = "enlist" - or - wrong = "enourmous" and right = "enormous" - or - wrong = "enourmously" and right = "enormously" - or - wrong = "ensconsed" and right = "ensconced" - or - wrong = "entaglements" and right = "entanglements" - or - wrong = "enteratinment" and right = "entertainment" - or - wrong = "enthusiatic" and right = "enthusiastic" - or - wrong = "entitity" and right = "entity" - or - wrong = "entitlied" and right = "entitled" - or - wrong = "entrepeneur" and right = "entrepreneur" - or - wrong = "entrepeneurs" and right = "entrepreneurs" - or - wrong = "enviorment" and right = "environment" - or - wrong = "enviormental" and right = "environmental" - or - wrong = "enviormentally" and right = "environmentally" - or - wrong = "enviorments" and right = "environments" - or - wrong = "enviornment" and right = "environment" - or - wrong = "enviornmental" and right = "environmental" - or - wrong = "enviornmentalist" and right = "environmentalist" - or - wrong = "enviornmentally" and right = "environmentally" - or - wrong = "enviornments" and right = "environments" - or - wrong = "enviroment" and right = "environment" - or - wrong = "enviromental" and right = "environmental" - or - wrong = "enviromentalist" and right = "environmentalist" - or - wrong = "enviromentally" and right = "environmentally" - or - wrong = "enviroments" and right = "environments" - or - wrong = "environemnt" and right = "environment" - or - wrong = "envolutionary" and right = "evolutionary" - or - wrong = "envrionments" and right = "environments" - or - wrong = "enxt" and right = "next" - or - wrong = "epidsodes" and right = "episodes" - or - wrong = "epsiode" and right = "episode" - or - wrong = "equialent" and right = "equivalent" - or - wrong = "equilibium" and right = "equilibrium" - or - wrong = "equilibrum" and right = "equilibrium" - or - wrong = "equiped" and right = "equipped" - or - wrong = "equippment" and right = "equipment" - or - wrong = "equitorial" and right = "equatorial" - or - wrong = "equivelant" and right = "equivalent" - or - wrong = "equivelent" and right = "equivalent" - or - wrong = "equivilant" and right = "equivalent" - or - wrong = "equivilent" and right = "equivalent" - or - wrong = "equivlalent" and right = "equivalent" - or - wrong = "erally" and right = "orally" - or - wrong = "erally" and right = "really" - or - wrong = "eratic" and right = "erratic" - or - wrong = "eratically" and right = "erratically" - or - wrong = "eraticly" and right = "erratically" - or - wrong = "erested" and right = "arrested" - or - wrong = "erested" and right = "erected" - or - wrong = "errupted" and right = "erupted" - or - wrong = "esential" and right = "essential" - or - wrong = "esitmated" and right = "estimated" - or - wrong = "esle" and right = "else" - or - wrong = "especialy" and right = "especially" - or - wrong = "essencial" and right = "essential" - or - wrong = "essense" and right = "essence" - or - wrong = "essentail" and right = "essential" - or - wrong = "essentialy" and right = "essentially" - or - wrong = "essentual" and right = "essential" - or - wrong = "essesital" and right = "essential" - or - wrong = "estabishes" and right = "establishes" - or - wrong = "establising" and right = "establishing" - or - wrong = "ethnocentricm" and right = "ethnocentrism" - or - wrong = "ethose" and right = "ethos" - or - wrong = "ethose" and right = "those" - or - wrong = "europian" and right = "european" - or - wrong = "europians" and right = "europeans" - or - wrong = "eurpean" and right = "european" - or - wrong = "eurpoean" and right = "european" - or - wrong = "evenhtually" and right = "eventually" - or - wrong = "eventally" and right = "eventually" - or - wrong = "eventially" and right = "eventually" - or - wrong = "eventualy" and right = "eventually" - or - wrong = "everthing" and right = "everything" - or - wrong = "everyting" and right = "everything" - or - wrong = "eveyr" and right = "every" - or - wrong = "evidentally" and right = "evidently" - or - wrong = "exagerate" and right = "exaggerate" - or - wrong = "exagerated" and right = "exaggerated" - or - wrong = "exagerates" and right = "exaggerates" - or - wrong = "exagerating" and right = "exaggerating" - or - wrong = "exagerrate" and right = "exaggerate" - or - wrong = "exagerrated" and right = "exaggerated" - or - wrong = "exagerrates" and right = "exaggerates" - or - wrong = "exagerrating" and right = "exaggerating" - or - wrong = "examinated" and right = "examined" - or - wrong = "exampt" and right = "exempt" - or - wrong = "exapansion" and right = "expansion" - or - wrong = "excact" and right = "exact" - or - wrong = "excange" and right = "exchange" - or - wrong = "excecute" and right = "execute" - or - wrong = "excecuted" and right = "executed" - or - wrong = "excecutes" and right = "executes" - or - wrong = "excecuting" and right = "executing" - or - wrong = "excecution" and right = "execution" - or - wrong = "excedded" and right = "exceeded" - or - wrong = "excelent" and right = "excellent" - or - wrong = "excell" and right = "excel" - or - wrong = "excellance" and right = "excellence" - or - wrong = "excellant" and right = "excellent" - or - wrong = "excells" and right = "excels" - or - wrong = "excercise" and right = "exercise" - or - wrong = "exchanching" and right = "exchanging" - or - wrong = "excisted" and right = "existed" - or - wrong = "exculsivly" and right = "exclusively" - or - wrong = "execising" and right = "exercising" - or - wrong = "exection" and right = "execution" - or - wrong = "exectued" and right = "executed" - or - wrong = "exeedingly" and right = "exceedingly" - or - wrong = "exelent" and right = "excellent" - or - wrong = "exellent" and right = "excellent" - or - wrong = "exemple" and right = "example" - or - wrong = "exept" and right = "except" - or - wrong = "exeptional" and right = "exceptional" - or - wrong = "exerbate" and right = "exacerbate" - or - wrong = "exerbated" and right = "exacerbated" - or - wrong = "exerciese" and right = "exercises" - or - wrong = "exerpt" and right = "excerpt" - or - wrong = "exerpts" and right = "excerpts" - or - wrong = "exersize" and right = "exercise" - or - wrong = "exerternal" and right = "external" - or - wrong = "exhalted" and right = "exalted" - or - wrong = "exhibtion" and right = "exhibition" - or - wrong = "exibition" and right = "exhibition" - or - wrong = "exibitions" and right = "exhibitions" - or - wrong = "exicting" and right = "exciting" - or - wrong = "exinct" and right = "extinct" - or - wrong = "existance" and right = "existence" - or - wrong = "existant" and right = "existent" - or - wrong = "existince" and right = "existence" - or - wrong = "exliled" and right = "exiled" - or - wrong = "exludes" and right = "excludes" - or - wrong = "exmaple" and right = "example" - or - wrong = "exonorate" and right = "exonerate" - or - wrong = "exoskelaton" and right = "exoskeleton" - or - wrong = "expalin" and right = "explain" - or - wrong = "expatriot" and right = "expatriate" - or - wrong = "expeced" and right = "expected" - or - wrong = "expecially" and right = "especially" - or - wrong = "expeditonary" and right = "expeditionary" - or - wrong = "expeiments" and right = "experiments" - or - wrong = "expell" and right = "expel" - or - wrong = "expells" and right = "expels" - or - wrong = "experiance" and right = "experience" - or - wrong = "experianced" and right = "experienced" - or - wrong = "expession" and right = "expression" - or - wrong = "expessions" and right = "expressions" - or - wrong = "expiditions" and right = "expeditions" - or - wrong = "expierence" and right = "experience" - or - wrong = "explaination" and right = "explanation" - or - wrong = "explaning" and right = "explaining" - or - wrong = "explictly" and right = "explicitly" - or - wrong = "exploititive" and right = "exploitative" - or - wrong = "explotation" and right = "exploitation" - or - wrong = "expropiated" and right = "expropriated" - or - wrong = "expropiation" and right = "expropriation" - or - wrong = "exressed" and right = "expressed" - or - wrong = "extemely" and right = "extremely" - or - wrong = "extened" and right = "extended" - or - wrong = "extention" and right = "extension" - or - wrong = "extentions" and right = "extensions" - or - wrong = "extered" and right = "exerted" - or - wrong = "extermist" and right = "extremist" - or - wrong = "extint" and right = "extant" - or - wrong = "extint" and right = "extinct" - or - wrong = "extracter" and right = "extractor" - or - wrong = "extradiction" and right = "extradition" - or - wrong = "extraterrestial" and right = "extraterrestrial" - or - wrong = "extraterrestials" and right = "extraterrestrials" - or - wrong = "extravagent" and right = "extravagant" - or - wrong = "extrememly" and right = "extremely" - or - wrong = "extremeophile" and right = "extremophile" - or - wrong = "extremly" and right = "extremely" - or - wrong = "extrordinarily" and right = "extraordinarily" - or - wrong = "extrordinary" and right = "extraordinary" - or - wrong = "eyar" and right = "eyas" - or - wrong = "eyar" and right = "year" - or - wrong = "eyars" and right = "eyas" - or - wrong = "eyars" and right = "years" - or - wrong = "eyasr" and right = "eyas" - or - wrong = "eyasr" and right = "years" - or - wrong = "faciliate" and right = "facilitate" - or - wrong = "faciliated" and right = "facilitated" - or - wrong = "faciliates" and right = "facilitates" - or - wrong = "facilites" and right = "facilities" - or - wrong = "facillitate" and right = "facilitate" - or - wrong = "facinated" and right = "fascinated" - or - wrong = "facist" and right = "fascist" - or - wrong = "familes" and right = "families" - or - wrong = "familliar" and right = "familiar" - or - wrong = "famoust" and right = "famous" - or - wrong = "fanatism" and right = "fanaticism" - or - wrong = "farenheit" and right = "fahrenheit" - or - wrong = "fatc" and right = "fact" - or - wrong = "faught" and right = "fought" - or - wrong = "favoutrable" and right = "favourable" - or - wrong = "feasable" and right = "feasible" - or - wrong = "febuary" and right = "february" - or - wrong = "feburary" and right = "february" - or - wrong = "fedreally" and right = "federally" - or - wrong = "femminist" and right = "feminist" - or - wrong = "feromone" and right = "pheromone" - or - wrong = "fertily" and right = "fertility" - or - wrong = "fianite" and right = "finite" - or - wrong = "fianlly" and right = "finally" - or - wrong = "ficticious" and right = "fictitious" - or - wrong = "fictious" and right = "fictitious" - or - wrong = "fidn" and right = "find" - or - wrong = "fiel" and right = "feel" - or - wrong = "fiel" and right = "field" - or - wrong = "fiel" and right = "file" - or - wrong = "fiel" and right = "phial" - or - wrong = "fiels" and right = "feels" - or - wrong = "fiels" and right = "fields" - or - wrong = "fiels" and right = "files" - or - wrong = "fiels" and right = "phials" - or - wrong = "fiercly" and right = "fiercely" - or - wrong = "fightings" and right = "fighting" - or - wrong = "filiament" and right = "filament" - or - wrong = "fimilies" and right = "families" - or - wrong = "finacial" and right = "financial" - or - wrong = "finaly" and right = "finally" - or - wrong = "financialy" and right = "financially" - or - wrong = "firends" and right = "friends" - or - wrong = "firts" and right = "first" - or - wrong = "firts" and right = "flirts" - or - wrong = "fisionable" and right = "fissionable" - or - wrong = "flamable" and right = "flammable" - or - wrong = "flawess" and right = "flawless" - or - wrong = "fleed" and right = "fled" - or - wrong = "fleed" and right = "freed" - or - wrong = "flemmish" and right = "flemish" - or - wrong = "florescent" and right = "fluorescent" - or - wrong = "flourescent" and right = "fluorescent" - or - wrong = "flourine" and right = "fluorine" - or - wrong = "flourishment" and right = "flourishing" - or - wrong = "fluorish" and right = "flourish" - or - wrong = "follwoing" and right = "following" - or - wrong = "folowing" and right = "following" - or - wrong = "fomed" and right = "formed" - or - wrong = "fomr" and right = "form" - or - wrong = "fomr" and right = "from" - or - wrong = "fonetic" and right = "phonetic" - or - wrong = "fontrier" and right = "fontier" - or - wrong = "foootball" and right = "football" - or - wrong = "forbad" and right = "forbade" - or - wrong = "forbiden" and right = "forbidden" - or - wrong = "foreward" and right = "foreword" - or - wrong = "forfiet" and right = "forfeit" - or - wrong = "forhead" and right = "forehead" - or - wrong = "foriegn" and right = "foreign" - or - wrong = "formalhaut" and right = "fomalhaut" - or - wrong = "formallize" and right = "formalize" - or - wrong = "formallized" and right = "formalized" - or - wrong = "formaly" and right = "formally" - or - wrong = "formaly" and right = "formerly" - or - wrong = "formelly" and right = "formerly" - or - wrong = "formidible" and right = "formidable" - or - wrong = "formost" and right = "foremost" - or - wrong = "forsaw" and right = "foresaw" - or - wrong = "forseeable" and right = "foreseeable" - or - wrong = "fortelling" and right = "foretelling" - or - wrong = "forunner" and right = "forerunner" - or - wrong = "foucs" and right = "focus" - or - wrong = "foudn" and right = "found" - or - wrong = "fougth" and right = "fought" - or - wrong = "foundaries" and right = "foundries" - or - wrong = "foundary" and right = "foundry" - or - wrong = "foundland" and right = "newfoundland" - or - wrong = "fourties" and right = "forties" - or - wrong = "fourty" and right = "forty" - or - wrong = "fouth" and right = "fourth" - or - wrong = "foward" and right = "forward" - or - wrong = "framwork" and right = "framework" - or - wrong = "fransiscan" and right = "franciscan" - or - wrong = "fransiscans" and right = "franciscans" - or - wrong = "freind" and right = "friend" - or - wrong = "freindly" and right = "friendly" - or - wrong = "frequentily" and right = "frequently" - or - wrong = "frome" and right = "from" - or - wrong = "fromed" and right = "formed" - or - wrong = "froniter" and right = "frontier" - or - wrong = "fucntion" and right = "function" - or - wrong = "fucntioning" and right = "functioning" - or - wrong = "fufill" and right = "fulfill" - or - wrong = "fufilled" and right = "fulfilled" - or - wrong = "fulfiled" and right = "fulfilled" - or - wrong = "fullfill" and right = "fulfill" - or - wrong = "fullfilled" and right = "fulfilled" - or - wrong = "funcion" and right = "function" - or - wrong = "fundametal" and right = "fundamental" - or - wrong = "fundametals" and right = "fundamentals" - or - wrong = "funguses" and right = "fungi" - or - wrong = "funtion" and right = "function" - or - wrong = "funtions" and right = "functions" - or - wrong = "furuther" and right = "further" - or - wrong = "futher" and right = "further" - or - wrong = "futhermore" and right = "furthermore" - or - wrong = "futhroc" and right = "futhark" - or - wrong = "futhroc" and right = "futhorc" - or - wrong = "gae" and right = "gael" - or - wrong = "gae" and right = "gale" - or - wrong = "gae" and right = "game" - or - wrong = "galatic" and right = "galactic" - or - wrong = "galations" and right = "galatians" - or - wrong = "gallaxies" and right = "galaxies" - or - wrong = "galvinized" and right = "galvanized" - or - wrong = "ganerate" and right = "generate" - or - wrong = "ganes" and right = "games" - or - wrong = "ganster" and right = "gangster" - or - wrong = "garantee" and right = "guarantee" - or - wrong = "garanteed" and right = "guaranteed" - or - wrong = "garantees" and right = "guarantees" - or - wrong = "garnison" and right = "garrison" - or - wrong = "gaurantee" and right = "guarantee" - or - wrong = "gauranteed" and right = "guaranteed" - or - wrong = "gaurantees" and right = "guarantees" - or - wrong = "gaurd" and right = "gourd" - or - wrong = "gaurd" and right = "guard" - or - wrong = "gaurentee" and right = "guarantee" - or - wrong = "gaurenteed" and right = "guaranteed" - or - wrong = "gaurentees" and right = "guarantees" - or - wrong = "geneological" and right = "genealogical" - or - wrong = "geneologies" and right = "genealogies" - or - wrong = "geneology" and right = "genealogy" - or - wrong = "generaly" and right = "generally" - or - wrong = "generatting" and right = "generating" - or - wrong = "genialia" and right = "genitalia" - or - wrong = "geographicial" and right = "geographical" - or - wrong = "geometrician" and right = "geometer" - or - wrong = "geometricians" and right = "geometers" - or - wrong = "gerat" and right = "great" - or - wrong = "ghandi" and right = "gandhi" - or - wrong = "glamourous" and right = "glamorous" - or - wrong = "glight" and right = "flight" - or - wrong = "gnawwed" and right = "gnawed" - or - wrong = "godess" and right = "goddess" - or - wrong = "godesses" and right = "goddesses" - or - wrong = "godounov" and right = "godunov" - or - wrong = "gogin" and right = "gauguin" - or - wrong = "gogin" and right = "going" - or - wrong = "goign" and right = "going" - or - wrong = "gonig" and right = "going" - or - wrong = "gothenberg" and right = "gothenburg" - or - wrong = "gottleib" and right = "gottlieb" - or - wrong = "gouvener" and right = "governor" - or - wrong = "govement" and right = "government" - or - wrong = "govenment" and right = "government" - or - wrong = "govenrment" and right = "government" - or - wrong = "goverance" and right = "governance" - or - wrong = "goverment" and right = "government" - or - wrong = "govermental" and right = "governmental" - or - wrong = "governer" and right = "governor" - or - wrong = "governmnet" and right = "government" - or - wrong = "govorment" and right = "government" - or - wrong = "govormental" and right = "governmental" - or - wrong = "govornment" and right = "government" - or - wrong = "gracefull" and right = "graceful" - or - wrong = "graet" and right = "great" - or - wrong = "grafitti" and right = "graffiti" - or - wrong = "gramatically" and right = "grammatically" - or - wrong = "grammaticaly" and right = "grammatically" - or - wrong = "grammer" and right = "grammar" - or - wrong = "grat" and right = "great" - or - wrong = "gratuitious" and right = "gratuitous" - or - wrong = "greatful" and right = "grateful" - or - wrong = "greatfully" and right = "gratefully" - or - wrong = "greif" and right = "grief" - or - wrong = "gridles" and right = "griddles" - or - wrong = "gropu" and right = "group" - or - wrong = "grwo" and right = "grow" - or - wrong = "guaduloupe" and right = "guadalupe" - or - wrong = "guaduloupe" and right = "guadeloupe" - or - wrong = "guadulupe" and right = "guadalupe" - or - wrong = "guadulupe" and right = "guadeloupe" - or - wrong = "guage" and right = "gauge" - or - wrong = "guarentee" and right = "guarantee" - or - wrong = "guarenteed" and right = "guaranteed" - or - wrong = "guarentees" and right = "guarantees" - or - wrong = "guatamala" and right = "guatemala" - or - wrong = "guatamalan" and right = "guatemalan" - or - wrong = "guerrila" and right = "guerrilla" - or - wrong = "guerrilas" and right = "guerrillas" - or - wrong = "guidence" and right = "guidance" - or - wrong = "guilia" and right = "giulia" - or - wrong = "guilio" and right = "giulio" - or - wrong = "guiness" and right = "guinness" - or - wrong = "guiseppe" and right = "giuseppe" - or - wrong = "gunanine" and right = "guanine" - or - wrong = "gurantee" and right = "guarantee" - or - wrong = "guranteed" and right = "guaranteed" - or - wrong = "gurantees" and right = "guarantees" - or - wrong = "guttaral" and right = "guttural" - or - wrong = "gutteral" and right = "guttural" - or - wrong = "habaeus" and right = "habeas" - or - wrong = "habeus" and right = "habeas" - or - wrong = "habsbourg" and right = "habsburg" - or - wrong = "haemorrage" and right = "haemorrhage" - or - wrong = "haev" and right = "have" - or - wrong = "haev" and right = "heave" - or - wrong = "halarious" and right = "hilarious" - or - wrong = "hallowean" and right = "halloween" - or - wrong = "halp" and right = "help" - or - wrong = "hander" and right = "handler" - or - wrong = "hapen" and right = "happen" - or - wrong = "hapened" and right = "happened" - or - wrong = "hapening" and right = "happening" - or - wrong = "happend" and right = "happened" - or - wrong = "happended" and right = "happened" - or - wrong = "happenned" and right = "happened" - or - wrong = "harased" and right = "harassed" - or - wrong = "harases" and right = "harasses" - or - wrong = "harasment" and right = "harassment" - or - wrong = "harasments" and right = "harassments" - or - wrong = "harassement" and right = "harassment" - or - wrong = "harras" and right = "harass" - or - wrong = "harrased" and right = "harassed" - or - wrong = "harrases" and right = "harasses" - or - wrong = "harrasing" and right = "harassing" - or - wrong = "harrasment" and right = "harassment" - or - wrong = "harrasments" and right = "harassments" - or - wrong = "harrassed" and right = "harassed" - or - wrong = "harrasses" and right = "harassed" - or - wrong = "harrassing" and right = "harassing" - or - wrong = "harrassment" and right = "harassment" - or - wrong = "harrassments" and right = "harassments" - or - wrong = "hatian" and right = "haitian" - or - wrong = "haviest" and right = "heaviest" - or - wrong = "headquarer" and right = "headquarter" - or - wrong = "headquater" and right = "headquarter" - or - wrong = "headquatered" and right = "headquartered" - or - wrong = "headquaters" and right = "headquarters" - or - wrong = "healthercare" and right = "healthcare" - or - wrong = "heared" and right = "heard" - or - wrong = "heathy" and right = "healthy" - or - wrong = "heidelburg" and right = "heidelberg" - or - wrong = "heigher" and right = "higher" - or - wrong = "heirarchy" and right = "hierarchy" - or - wrong = "heiroglyphics" and right = "hieroglyphics" - or - wrong = "helment" and right = "helmet" - or - wrong = "helpfull" and right = "helpful" - or - wrong = "helpped" and right = "helped" - or - wrong = "hemmorhage" and right = "hemorrhage" - or - wrong = "herad" and right = "heard" - or - wrong = "herad" and right = "hera" - or - wrong = "heridity" and right = "heredity" - or - wrong = "heroe" and right = "hero" - or - wrong = "heros" and right = "heroes" - or - wrong = "hertiage" and right = "heritage" - or - wrong = "hertzs" and right = "hertz" - or - wrong = "hesistant" and right = "hesitant" - or - wrong = "heterogenous" and right = "heterogeneous" - or - wrong = "hieght" and right = "height" - or - wrong = "hierachical" and right = "hierarchical" - or - wrong = "hierachies" and right = "hierarchies" - or - wrong = "hierachy" and right = "hierarchy" - or - wrong = "hierarcical" and right = "hierarchical" - or - wrong = "hierarcy" and right = "hierarchy" - or - wrong = "hieroglph" and right = "hieroglyph" - or - wrong = "hieroglphs" and right = "hieroglyphs" - or - wrong = "higer" and right = "higher" - or - wrong = "higest" and right = "highest" - or - wrong = "higway" and right = "highway" - or - wrong = "hillarious" and right = "hilarious" - or - wrong = "himselv" and right = "himself" - or - wrong = "hinderance" and right = "hindrance" - or - wrong = "hinderence" and right = "hindrance" - or - wrong = "hindrence" and right = "hindrance" - or - wrong = "hipopotamus" and right = "hippopotamus" - or - wrong = "hismelf" and right = "himself" - or - wrong = "histocompatability" and right = "histocompatibility" - or - wrong = "historicians" and right = "historians" - or - wrong = "holf" and right = "hold" - or - wrong = "holliday" and right = "holiday" - or - wrong = "homogeneize" and right = "homogenize" - or - wrong = "homogeneized" and right = "homogenized" - or - wrong = "honory" and right = "honorary" - or - wrong = "horrifing" and right = "horrifying" - or - wrong = "hosited" and right = "hoisted" - or - wrong = "hospitible" and right = "hospitable" - or - wrong = "hounour" and right = "honour" - or - wrong = "housr" and right = "hours" - or - wrong = "housr" and right = "house" - or - wrong = "howver" and right = "however" - or - wrong = "hsitorians" and right = "historians" - or - wrong = "hstory" and right = "history" - or - wrong = "hten" and right = "hen" - or - wrong = "hten" and right = "the" - or - wrong = "hten" and right = "then" - or - wrong = "htere" and right = "here" - or - wrong = "htere" and right = "there" - or - wrong = "htey" and right = "they" - or - wrong = "htikn" and right = "think" - or - wrong = "hting" and right = "thing" - or - wrong = "htink" and right = "think" - or - wrong = "htis" and right = "this" - or - wrong = "humer" and right = "humor" - or - wrong = "humer" and right = "humour" - or - wrong = "humerous" and right = "humerus" - or - wrong = "humerous" and right = "humorous" - or - wrong = "huminoid" and right = "humanoid" - or - wrong = "humoural" and right = "humoral" - or - wrong = "humurous" and right = "humorous" - or - wrong = "husban" and right = "husband" - or - wrong = "hvae" and right = "have" - or - wrong = "hvaing" and right = "having" - or - wrong = "hvea" and right = "have" - or - wrong = "hvea" and right = "heave" - or - wrong = "hwihc" and right = "which" - or - wrong = "hwile" and right = "while" - or - wrong = "hwole" and right = "whole" - or - wrong = "hydogen" and right = "hydrogen" - or - wrong = "hydropile" and right = "hydrophile" - or - wrong = "hydropilic" and right = "hydrophilic" - or - wrong = "hydropobe" and right = "hydrophobe" - or - wrong = "hydropobic" and right = "hydrophobic" - or - wrong = "hygeine" and right = "hygiene" - or - wrong = "hyjack" and right = "hijack" - or - wrong = "hyjacking" and right = "hijacking" - or - wrong = "hypocracy" and right = "hypocrisy" - or - wrong = "hypocrasy" and right = "hypocrisy" - or - wrong = "hypocricy" and right = "hypocrisy" - or - wrong = "hypocrit" and right = "hypocrite" - or - wrong = "hypocrits" and right = "hypocrites" - or - wrong = "iconclastic" and right = "iconoclastic" - or - wrong = "idaeidae" and right = "idea" - or - wrong = "idaes" and right = "ideas" - or - wrong = "idealogies" and right = "ideologies" - or - wrong = "idealogy" and right = "ideology" - or - wrong = "identicial" and right = "identical" - or - wrong = "identifers" and right = "identifiers" - or - wrong = "ideosyncratic" and right = "idiosyncratic" - or - wrong = "idesa" and right = "ideas" - or - wrong = "idesa" and right = "ides" - or - wrong = "idiosyncracy" and right = "idiosyncrasy" - or - wrong = "ihaca" and right = "ithaca" - or - wrong = "illegimacy" and right = "illegitimacy" - or - wrong = "illegitmate" and right = "illegitimate" - or - wrong = "illess" and right = "illness" - or - wrong = "illiegal" and right = "illegal" - or - wrong = "illution" and right = "illusion" - or - wrong = "ilness" and right = "illness" - or - wrong = "ilogical" and right = "illogical" - or - wrong = "imagenary" and right = "imaginary" - or - wrong = "imagin" and right = "imagine" - or - wrong = "imaginery" and right = "imagery" - or - wrong = "imaginery" and right = "imaginary" - or - wrong = "imanent" and right = "eminent" - or - wrong = "imanent" and right = "imminent" - or - wrong = "imcomplete" and right = "incomplete" - or - wrong = "imediately" and right = "immediately" - or - wrong = "imense" and right = "immense" - or - wrong = "imigrant" and right = "emigrant" - or - wrong = "imigrant" and right = "immigrant" - or - wrong = "imigrated" and right = "emigrated" - or - wrong = "imigrated" and right = "immigrated" - or - wrong = "imigration" and right = "emigration" - or - wrong = "imigration" and right = "immigration" - or - wrong = "iminent" and right = "eminent" - or - wrong = "iminent" and right = "immanent" - or - wrong = "iminent" and right = "imminent" - or - wrong = "immediatley" and right = "immediately" - or - wrong = "immediatly" and right = "immediately" - or - wrong = "immidately" and right = "immediately" - or - wrong = "immidiately" and right = "immediately" - or - wrong = "immitate" and right = "imitate" - or - wrong = "immitated" and right = "imitated" - or - wrong = "immitating" and right = "imitating" - or - wrong = "immitator" and right = "imitator" - or - wrong = "immunosupressant" and right = "immunosuppressant" - or - wrong = "impecabbly" and right = "impeccably" - or - wrong = "impedence" and right = "impedance" - or - wrong = "implamenting" and right = "implementing" - or - wrong = "impliment" and right = "implement" - or - wrong = "implimented" and right = "implemented" - or - wrong = "imploys" and right = "employs" - or - wrong = "importamt" and right = "important" - or - wrong = "impressario" and right = "impresario" - or - wrong = "imprioned" and right = "imprisoned" - or - wrong = "imprisonned" and right = "imprisoned" - or - wrong = "improvision" and right = "improvisation" - or - wrong = "improvments" and right = "improvements" - or - wrong = "inablility" and right = "inability" - or - wrong = "inaccessable" and right = "inaccessible" - or - wrong = "inadiquate" and right = "inadequate" - or - wrong = "inadquate" and right = "inadequate" - or - wrong = "inadvertant" and right = "inadvertent" - or - wrong = "inadvertantly" and right = "inadvertently" - or - wrong = "inagurated" and right = "inaugurated" - or - wrong = "inaguration" and right = "inauguration" - or - wrong = "inappropiate" and right = "inappropriate" - or - wrong = "inaugures" and right = "inaugurates" - or - wrong = "inbalance" and right = "imbalance" - or - wrong = "inbalanced" and right = "imbalanced" - or - wrong = "inbetween" and right = "between" - or - wrong = "incarcirated" and right = "incarcerated" - or - wrong = "incidentially" and right = "incidentally" - or - wrong = "incidently" and right = "incidentally" - or - wrong = "inclreased" and right = "increased" - or - wrong = "includ" and right = "include" - or - wrong = "includng" and right = "including" - or - wrong = "incompatabilities" and right = "incompatibilities" - or - wrong = "incompatability" and right = "incompatibility" - or - wrong = "incompatable" and right = "incompatible" - or - wrong = "incompatablities" and right = "incompatibilities" - or - wrong = "incompatablity" and right = "incompatibility" - or - wrong = "incompatiblities" and right = "incompatibilities" - or - wrong = "incompatiblity" and right = "incompatibility" - or - wrong = "incompetance" and right = "incompetence" - or - wrong = "incompetant" and right = "incompetent" - or - wrong = "incomptable" and right = "incompatible" - or - wrong = "incomptetent" and right = "incompetent" - or - wrong = "inconsistant" and right = "inconsistent" - or - wrong = "incoroporated" and right = "incorporated" - or - wrong = "incorperation" and right = "incorporation" - or - wrong = "incorportaed" and right = "incorporated" - or - wrong = "incorprates" and right = "incorporates" - or - wrong = "incorruptable" and right = "incorruptible" - or - wrong = "incramentally" and right = "incrementally" - or - wrong = "increadible" and right = "incredible" - or - wrong = "incredable" and right = "incredible" - or - wrong = "inctroduce" and right = "introduce" - or - wrong = "inctroduced" and right = "introduced" - or - wrong = "incuding" and right = "including" - or - wrong = "incunabla" and right = "incunabula" - or - wrong = "indefinately" and right = "indefinitely" - or - wrong = "indefineable" and right = "undefinable" - or - wrong = "indefinitly" and right = "indefinitely" - or - wrong = "indentical" and right = "identical" - or - wrong = "indepedantly" and right = "independently" - or - wrong = "indepedence" and right = "independence" - or - wrong = "independance" and right = "independence" - or - wrong = "independant" and right = "independent" - or - wrong = "independantly" and right = "independently" - or - wrong = "independece" and right = "independence" - or - wrong = "independendet" and right = "independent" - or - wrong = "indespensable" and right = "indispensable" - or - wrong = "indespensible" and right = "indispensable" - or - wrong = "indictement" and right = "indictment" - or - wrong = "indigineous" and right = "indigenous" - or - wrong = "indipendence" and right = "independence" - or - wrong = "indipendent" and right = "independent" - or - wrong = "indipendently" and right = "independently" - or - wrong = "indispensible" and right = "indispensable" - or - wrong = "indisputible" and right = "indisputable" - or - wrong = "indisputibly" and right = "indisputably" - or - wrong = "indite" and right = "indict" - or - wrong = "individualy" and right = "individually" - or - wrong = "indpendent" and right = "independent" - or - wrong = "indpendently" and right = "independently" - or - wrong = "indulgue" and right = "indulge" - or - wrong = "indutrial" and right = "industrial" - or - wrong = "indviduals" and right = "individuals" - or - wrong = "inefficienty" and right = "inefficiently" - or - wrong = "inevatible" and right = "inevitable" - or - wrong = "inevitible" and right = "inevitable" - or - wrong = "inevititably" and right = "inevitably" - or - wrong = "infalability" and right = "infallibility" - or - wrong = "infallable" and right = "infallible" - or - wrong = "infectuous" and right = "infectious" - or - wrong = "infered" and right = "inferred" - or - wrong = "infilitrate" and right = "infiltrate" - or - wrong = "infilitrated" and right = "infiltrated" - or - wrong = "infilitration" and right = "infiltration" - or - wrong = "infinit" and right = "infinite" - or - wrong = "inflamation" and right = "inflammation" - or - wrong = "influencial" and right = "influential" - or - wrong = "influented" and right = "influenced" - or - wrong = "infomation" and right = "information" - or - wrong = "informtion" and right = "information" - or - wrong = "infrantryman" and right = "infantryman" - or - wrong = "infrigement" and right = "infringement" - or - wrong = "ingenius" and right = "ingenious" - or - wrong = "ingreediants" and right = "ingredients" - or - wrong = "inhabitans" and right = "inhabitants" - or - wrong = "inherantly" and right = "inherently" - or - wrong = "inheritage" and right = "heritage" - or - wrong = "inheritage" and right = "inheritance" - or - wrong = "inheritence" and right = "inheritance" - or - wrong = "inital" and right = "initial" - or - wrong = "initalize" and right = "initialize" - or - wrong = "initally" and right = "initially" - or - wrong = "initation" and right = "initiation" - or - wrong = "initiaitive" and right = "initiative" - or - wrong = "inlcuding" and right = "including" - or - wrong = "inmigrant" and right = "immigrant" - or - wrong = "inmigrants" and right = "immigrants" - or - wrong = "innoculated" and right = "inoculated" - or - wrong = "inocence" and right = "innocence" - or - wrong = "inofficial" and right = "unofficial" - or - wrong = "inot" and right = "into" - or - wrong = "inpeach" and right = "impeach" - or - wrong = "inpending" and right = "impending" - or - wrong = "inpenetrable" and right = "impenetrable" - or - wrong = "inpolite" and right = "impolite" - or - wrong = "inprisonment" and right = "imprisonment" - or - wrong = "inproving" and right = "improving" - or - wrong = "insectiverous" and right = "insectivorous" - or - wrong = "insensative" and right = "insensitive" - or - wrong = "inseperable" and right = "inseparable" - or - wrong = "insistance" and right = "insistence" - or - wrong = "insitution" and right = "institution" - or - wrong = "insitutions" and right = "institutions" - or - wrong = "inspite" and right = "inspire" - or - wrong = "instade" and right = "instead" - or - wrong = "instatance" and right = "instance" - or - wrong = "institue" and right = "institute" - or - wrong = "instuction" and right = "instruction" - or - wrong = "instuments" and right = "instruments" - or - wrong = "instutionalized" and right = "institutionalized" - or - wrong = "instutions" and right = "intuitions" - or - wrong = "insurence" and right = "insurance" - or - wrong = "intelectual" and right = "intellectual" - or - wrong = "inteligence" and right = "intelligence" - or - wrong = "inteligent" and right = "intelligent" - or - wrong = "intenational" and right = "international" - or - wrong = "intented" and right = "indented" - or - wrong = "intented" and right = "intended" - or - wrong = "intepretation" and right = "interpretation" - or - wrong = "intepretator" and right = "interpretor" - or - wrong = "interational" and right = "international" - or - wrong = "interbread" and right = "interbred" - or - wrong = "interbread" and right = "interbreed" - or - wrong = "interchangable" and right = "interchangeable" - or - wrong = "interchangably" and right = "interchangeably" - or - wrong = "intercontinential" and right = "intercontinental" - or - wrong = "intercontinetal" and right = "intercontinental" - or - wrong = "intered" and right = "interned" - or - wrong = "intered" and right = "interred" - or - wrong = "interelated" and right = "interrelated" - or - wrong = "interferance" and right = "interference" - or - wrong = "interfereing" and right = "interfering" - or - wrong = "intergrated" and right = "integrated" - or - wrong = "intergration" and right = "integration" - or - wrong = "interm" and right = "interim" - or - wrong = "internation" and right = "international" - or - wrong = "interpet" and right = "interpret" - or - wrong = "interrim" and right = "interim" - or - wrong = "interrugum" and right = "interregnum" - or - wrong = "intertaining" and right = "entertaining" - or - wrong = "interupt" and right = "interrupt" - or - wrong = "intervines" and right = "intervenes" - or - wrong = "intevene" and right = "intervene" - or - wrong = "intial" and right = "initial" - or - wrong = "intialize" and right = "initialize" - or - wrong = "intialized" and right = "initialized" - or - wrong = "intially" and right = "initially" - or - wrong = "intrduced" and right = "introduced" - or - wrong = "intrest" and right = "interest" - or - wrong = "introdued" and right = "introduced" - or - wrong = "intruduced" and right = "introduced" - or - wrong = "intrument" and right = "instrument" - or - wrong = "intrumental" and right = "instrumental" - or - wrong = "intruments" and right = "instruments" - or - wrong = "intrusted" and right = "entrusted" - or - wrong = "intutive" and right = "intuitive" - or - wrong = "intutively" and right = "intuitively" - or - wrong = "inudstry" and right = "industry" - or - wrong = "inumerable" and right = "enumerable" - or - wrong = "inumerable" and right = "innumerable" - or - wrong = "inventer" and right = "inventor" - or - wrong = "invertibrates" and right = "invertebrates" - or - wrong = "investingate" and right = "investigate" - or - wrong = "involvment" and right = "involvement" - or - wrong = "irelevent" and right = "irrelevant" - or - wrong = "iresistable" and right = "irresistible" - or - wrong = "iresistably" and right = "irresistibly" - or - wrong = "iresistible" and right = "irresistible" - or - wrong = "iresistibly" and right = "irresistibly" - or - wrong = "iritable" and right = "irritable" - or - wrong = "iritated" and right = "irritated" - or - wrong = "ironicly" and right = "ironically" - or - wrong = "irregardless" and right = "regardless" - or - wrong = "irrelevent" and right = "irrelevant" - or - wrong = "irreplacable" and right = "irreplaceable" - or - wrong = "irresistable" and right = "irresistible" - or - wrong = "irresistably" and right = "irresistibly" - or - wrong = "israelies" and right = "israelis" - or - wrong = "issueing" and right = "issuing" - or - wrong = "itnroduced" and right = "introduced" - or - wrong = "iunior" and right = "junior" - or - wrong = "iwll" and right = "will" - or - wrong = "iwth" and right = "with" - or - wrong = "janurary" and right = "january" - or - wrong = "januray" and right = "january" - or - wrong = "japanes" and right = "japanese" - or - wrong = "jaques" and right = "jacques" - or - wrong = "jeapardy" and right = "jeopardy" - or - wrong = "jewllery" and right = "jewellery" - or - wrong = "johanine" and right = "johannine" - or - wrong = "jorunal" and right = "journal" - or - wrong = "jospeh" and right = "joseph" - or - wrong = "jouney" and right = "journey" - or - wrong = "journied" and right = "journeyed" - or - wrong = "journies" and right = "journeys" - or - wrong = "jstu" and right = "just" - or - wrong = "jsut" and right = "just" - or - wrong = "juadaism" and right = "judaism" - or - wrong = "juadism" and right = "judaism" - or - wrong = "judical" and right = "judicial" - or - wrong = "judisuary" and right = "judiciary" - or - wrong = "juducial" and right = "judicial" - or - wrong = "juristiction" and right = "jurisdiction" - or - wrong = "juristictions" and right = "jurisdictions" - or - wrong = "kindergarden" and right = "kindergarten" - or - wrong = "klenex" and right = "kleenex" - or - wrong = "knifes" and right = "knives" - or - wrong = "knive" and right = "knife" - or - wrong = "knowlege" and right = "knowledge" - or - wrong = "knowlegeable" and right = "knowledgeable" - or - wrong = "knwo" and right = "know" - or - wrong = "knwos" and right = "knows" - or - wrong = "konw" and right = "know" - or - wrong = "konws" and right = "knows" - or - wrong = "kwno" and right = "know" - or - wrong = "labatory" and right = "laboratory" - or - wrong = "labatory" and right = "lavatory" - or - wrong = "labled" and right = "labeled" - or - wrong = "labled" and right = "labelled" - or - wrong = "labratory" and right = "laboratory" - or - wrong = "laguage" and right = "language" - or - wrong = "laguages" and right = "languages" - or - wrong = "langage" and right = "language" - or - wrong = "langauge" and right = "language" - or - wrong = "larg" and right = "large" - or - wrong = "largst" and right = "largest" - or - wrong = "larrry" and right = "larry" - or - wrong = "lastr" and right = "last" - or - wrong = "lattitude" and right = "latitude" - or - wrong = "launchs" and right = "launch" - or - wrong = "launchs" and right = "launches" - or - wrong = "launhed" and right = "launched" - or - wrong = "lavae" and right = "larvae" - or - wrong = "layed" and right = "laid" - or - wrong = "lazyness" and right = "laziness" - or - wrong = "leage" and right = "league" - or - wrong = "leanr" and right = "lean" - or - wrong = "leanr" and right = "leaner" - or - wrong = "leanr" and right = "learn" - or - wrong = "leathal" and right = "lethal" - or - wrong = "lefted" and right = "left" - or - wrong = "legitamate" and right = "legitimate" - or - wrong = "legitmate" and right = "legitimate" - or - wrong = "leibnitz" and right = "leibniz" - or - wrong = "lengh" and right = "length" - or - wrong = "lenght" and right = "length" - or - wrong = "lengt" and right = "length" - or - wrong = "lenth" and right = "length" - or - wrong = "leran" and right = "learn" - or - wrong = "lerans" and right = "learns" - or - wrong = "leutenant" and right = "lieutenant" - or - wrong = "levetate" and right = "levitate" - or - wrong = "levetated" and right = "levitated" - or - wrong = "levetates" and right = "levitates" - or - wrong = "levetating" and right = "levitating" - or - wrong = "levle" and right = "level" - or - wrong = "liasion" and right = "liaison" - or - wrong = "liason" and right = "liaison" - or - wrong = "liasons" and right = "liaisons" - or - wrong = "libaries" and right = "libraries" - or - wrong = "libary" and right = "library" - or - wrong = "libell" and right = "libel" - or - wrong = "libguistic" and right = "linguistic" - or - wrong = "libguistics" and right = "linguistics" - or - wrong = "libitarianisn" and right = "libertarianism" - or - wrong = "lible" and right = "liable" - or - wrong = "lible" and right = "libel" - or - wrong = "lieing" and right = "lying" - or - wrong = "liek" and right = "like" - or - wrong = "liekd" and right = "liked" - or - wrong = "liesure" and right = "leisure" - or - wrong = "lieuenant" and right = "lieutenant" - or - wrong = "lieved" and right = "lived" - or - wrong = "liftime" and right = "lifetime" - or - wrong = "likelyhood" and right = "likelihood" - or - wrong = "linnaena" and right = "linnaean" - or - wrong = "lippizaner" and right = "lipizzaner" - or - wrong = "liquify" and right = "liquefy" - or - wrong = "liscense" and right = "licence" - or - wrong = "liscense" and right = "license" - or - wrong = "lisence" and right = "licence" - or - wrong = "lisence" and right = "license" - or - wrong = "lisense" and right = "licence" - or - wrong = "lisense" and right = "license" - or - wrong = "listners" and right = "listeners" - or - wrong = "litature" and right = "literature" - or - wrong = "literaly" and right = "literally" - or - wrong = "literture" and right = "literature" - or - wrong = "littel" and right = "little" - or - wrong = "litterally" and right = "literally" - or - wrong = "liuke" and right = "like" - or - wrong = "livley" and right = "lively" - or - wrong = "lmits" and right = "limits" - or - wrong = "loev" and right = "love" - or - wrong = "lonelyness" and right = "loneliness" - or - wrong = "longitudonal" and right = "longitudinal" - or - wrong = "lonley" and right = "lonely" - or - wrong = "lonly" and right = "lonely" - or - wrong = "lonly" and right = "only" - or - wrong = "loosing" and right = "losing" - or - wrong = "lotharingen" and right = "lothringen" - or - wrong = "lsat" and right = "last" - or - wrong = "lukid" and right = "likud" - or - wrong = "lveo" and right = "love" - or - wrong = "lvoe" and right = "love" - or - wrong = "lybia" and right = "libya" - or - wrong = "maching" and right = "machine" - or - wrong = "maching" and right = "marching" - or - wrong = "maching" and right = "matching" - or - wrong = "mackeral" and right = "mackerel" - or - wrong = "magasine" and right = "magazine" - or - wrong = "magincian" and right = "magician" - or - wrong = "magisine" and right = "magazine" - or - wrong = "magizine" and right = "magazine" - or - wrong = "magnificient" and right = "magnificent" - or - wrong = "magolia" and right = "magnolia" - or - wrong = "mailny" and right = "mainly" - or - wrong = "maintainance" and right = "maintenance" - or - wrong = "maintainence" and right = "maintenance" - or - wrong = "maintance" and right = "maintenance" - or - wrong = "maintenence" and right = "maintenance" - or - wrong = "maintinaing" and right = "maintaining" - or - wrong = "maintioned" and right = "mentioned" - or - wrong = "majoroty" and right = "majority" - or - wrong = "maked" and right = "made" - or - wrong = "maked" and right = "marked" - or - wrong = "makse" and right = "makes" - or - wrong = "malcom" and right = "malcolm" - or - wrong = "maltesian" and right = "maltese" - or - wrong = "mamal" and right = "mammal" - or - wrong = "mamalian" and right = "mammalian" - or - wrong = "managable" and right = "manageable" - or - wrong = "managable" and right = "manageably" - or - wrong = "managment" and right = "management" - or - wrong = "maneouvre" and right = "manoeuvre" - or - wrong = "maneouvred" and right = "manoeuvred" - or - wrong = "maneouvres" and right = "manoeuvres" - or - wrong = "maneouvring" and right = "manoeuvring" - or - wrong = "manisfestations" and right = "manifestations" - or - wrong = "manoeuverability" and right = "maneuverability" - or - wrong = "manouver" and right = "maneuver" - or - wrong = "manouver" and right = "manoeuvre" - or - wrong = "manouverability" and right = "maneuverability" - or - wrong = "manouverability" and right = "manoeuverability" - or - wrong = "manouverability" and right = "manoeuvrability" - or - wrong = "manouverable" and right = "maneuverable" - or - wrong = "manouverable" and right = "manoeuvrable" - or - wrong = "manouvers" and right = "maneuvers" - or - wrong = "manouvers" and right = "manoeuvres" - or - wrong = "mantained" and right = "maintained" - or - wrong = "manuever" and right = "maneuver" - or - wrong = "manuever" and right = "manoeuvre" - or - wrong = "manuevers" and right = "maneuvers" - or - wrong = "manuevers" and right = "manoeuvres" - or - wrong = "manufacturedd" and right = "manufactured" - or - wrong = "manufature" and right = "manufacture" - or - wrong = "manufatured" and right = "manufactured" - or - wrong = "manufaturing" and right = "manufacturing" - or - wrong = "manuver" and right = "maneuver" - or - wrong = "mariage" and right = "marriage" - or - wrong = "marjority" and right = "majority" - or - wrong = "markes" and right = "marks" - or - wrong = "marketting" and right = "marketing" - or - wrong = "marmelade" and right = "marmalade" - or - wrong = "marrage" and right = "marriage" - or - wrong = "marraige" and right = "marriage" - or - wrong = "marrtyred" and right = "martyred" - or - wrong = "marryied" and right = "married" - or - wrong = "massachussets" and right = "massachusetts" - or - wrong = "massachussetts" and right = "massachusetts" - or - wrong = "masterbation" and right = "masturbation" - or - wrong = "mataphysical" and right = "metaphysical" - or - wrong = "materalists" and right = "materialist" - or - wrong = "mathamatics" and right = "mathematics" - or - wrong = "mathematican" and right = "mathematician" - or - wrong = "mathematicas" and right = "mathematics" - or - wrong = "matheticians" and right = "mathematicians" - or - wrong = "mathmatically" and right = "mathematically" - or - wrong = "mathmatician" and right = "mathematician" - or - wrong = "mathmaticians" and right = "mathematicians" - or - wrong = "mccarthyst" and right = "mccarthyist" - or - wrong = "mchanics" and right = "mechanics" - or - wrong = "meaninng" and right = "meaning" - or - wrong = "mear" and right = "mare" - or - wrong = "mear" and right = "mere" - or - wrong = "mear" and right = "wear" - or - wrong = "mechandise" and right = "merchandise" - or - wrong = "medacine" and right = "medicine" - or - wrong = "medeival" and right = "medieval" - or - wrong = "medevial" and right = "medieval" - or - wrong = "mediciney" and right = "mediciny" - or - wrong = "medievel" and right = "medieval" - or - wrong = "mediterainnean" and right = "mediterranean" - or - wrong = "mediteranean" and right = "mediterranean" - or - wrong = "meerkrat" and right = "meerkat" - or - wrong = "melieux" and right = "milieux" - or - wrong = "membranaphone" and right = "membranophone" - or - wrong = "memeber" and right = "member" - or - wrong = "menally" and right = "mentally" - or - wrong = "meranda" and right = "miranda" - or - wrong = "meranda" and right = "veranda" - or - wrong = "mercentile" and right = "mercantile" - or - wrong = "mesage" and right = "message" - or - wrong = "messanger" and right = "messenger" - or - wrong = "messenging" and right = "messaging" - or - wrong = "messsage" and right = "message" - or - wrong = "metalic" and right = "metallic" - or - wrong = "metalurgic" and right = "metallurgic" - or - wrong = "metalurgical" and right = "metallurgical" - or - wrong = "metalurgy" and right = "metallurgy" - or - wrong = "metamorphysis" and right = "metamorphosis" - or - wrong = "metaphoricial" and right = "metaphorical" - or - wrong = "meterologist" and right = "meteorologist" - or - wrong = "meterology" and right = "meteorology" - or - wrong = "methaphor" and right = "metaphor" - or - wrong = "methaphors" and right = "metaphors" - or - wrong = "michagan" and right = "michigan" - or - wrong = "micoscopy" and right = "microscopy" - or - wrong = "midwifes" and right = "midwives" - or - wrong = "mileau" and right = "milieu" - or - wrong = "milennia" and right = "millennia" - or - wrong = "milennium" and right = "millennium" - or - wrong = "mileu" and right = "milieu" - or - wrong = "miliary" and right = "military" - or - wrong = "miligram" and right = "milligram" - or - wrong = "milion" and right = "million" - or - wrong = "miliraty" and right = "military" - or - wrong = "millenia" and right = "millennia" - or - wrong = "millenial" and right = "millennial" - or - wrong = "millenialism" and right = "millennialism" - or - wrong = "millenium" and right = "millennium" - or - wrong = "millepede" and right = "millipede" - or - wrong = "millioniare" and right = "millionaire" - or - wrong = "millitant" and right = "militant" - or - wrong = "millitary" and right = "military" - or - wrong = "millon" and right = "million" - or - wrong = "miltary" and right = "military" - or - wrong = "minature" and right = "miniature" - or - wrong = "minerial" and right = "mineral" - or - wrong = "ministery" and right = "ministry" - or - wrong = "minsitry" and right = "ministry" - or - wrong = "minstries" and right = "ministries" - or - wrong = "minstry" and right = "ministry" - or - wrong = "minumum" and right = "minimum" - or - wrong = "mirrorred" and right = "mirrored" - or - wrong = "miscelaneous" and right = "miscellaneous" - or - wrong = "miscellanious" and right = "miscellaneous" - or - wrong = "miscellanous" and right = "miscellaneous" - or - wrong = "mischeivous" and right = "mischievous" - or - wrong = "mischevious" and right = "mischievous" - or - wrong = "mischievious" and right = "mischievous" - or - wrong = "misdameanor" and right = "misdemeanor" - or - wrong = "misdameanors" and right = "misdemeanors" - or - wrong = "misdemenor" and right = "misdemeanor" - or - wrong = "misdemenors" and right = "misdemeanors" - or - wrong = "misfourtunes" and right = "misfortunes" - or - wrong = "misile" and right = "missile" - or - wrong = "misouri" and right = "missouri" - or - wrong = "mispell" and right = "misspell" - or - wrong = "mispelled" and right = "misspelled" - or - wrong = "mispelling" and right = "misspelling" - or - wrong = "missen" and right = "mizzen" - or - wrong = "missisipi" and right = "mississippi" - or - wrong = "missisippi" and right = "mississippi" - or - wrong = "missle" and right = "missile" - or - wrong = "missonary" and right = "missionary" - or - wrong = "misterious" and right = "mysterious" - or - wrong = "mistery" and right = "mystery" - or - wrong = "misteryous" and right = "mysterious" - or - wrong = "mkae" and right = "make" - or - wrong = "mkaes" and right = "makes" - or - wrong = "mkaing" and right = "making" - or - wrong = "mkea" and right = "make" - or - wrong = "moderm" and right = "modem" - or - wrong = "modle" and right = "model" - or - wrong = "moent" and right = "moment" - or - wrong = "moeny" and right = "money" - or - wrong = "mohammedans" and right = "muslims" - or - wrong = "moil" and right = "mohel" - or - wrong = "moil" and right = "soil" - or - wrong = "moleclues" and right = "molecules" - or - wrong = "momento" and right = "memento" - or - wrong = "monestaries" and right = "monasteries" - or - wrong = "monestary" and right = "monastery" - or - wrong = "monestary" and right = "monetary" - or - wrong = "monickers" and right = "monikers" - or - wrong = "monolite" and right = "monolithic" - or - wrong = "monserrat" and right = "montserrat" - or - wrong = "montains" and right = "mountains" - or - wrong = "montanous" and right = "mountainous" - or - wrong = "montnana" and right = "montana" - or - wrong = "monts" and right = "months" - or - wrong = "montypic" and right = "monotypic" - or - wrong = "moreso" and right = "more" - or - wrong = "morgage" and right = "mortgage" - or - wrong = "morisette" and right = "morissette" - or - wrong = "morrisette" and right = "morissette" - or - wrong = "morroccan" and right = "moroccan" - or - wrong = "morrocco" and right = "morocco" - or - wrong = "morroco" and right = "morocco" - or - wrong = "mortage" and right = "mortgage" - or - wrong = "mosture" and right = "moisture" - or - wrong = "motiviated" and right = "motivated" - or - wrong = "mounth" and right = "month" - or - wrong = "movei" and right = "movie" - or - wrong = "movment" and right = "movement" - or - wrong = "mroe" and right = "more" - or - wrong = "mucuous" and right = "mucous" - or - wrong = "muder" and right = "murder" - or - wrong = "mudering" and right = "murdering" - or - wrong = "muhammadan" and right = "muslim" - or - wrong = "multicultralism" and right = "multiculturalism" - or - wrong = "multipled" and right = "multiplied" - or - wrong = "multiplers" and right = "multipliers" - or - wrong = "munbers" and right = "numbers" - or - wrong = "muncipalities" and right = "municipalities" - or - wrong = "muncipality" and right = "municipality" - or - wrong = "munnicipality" and right = "municipality" - or - wrong = "muscels" and right = "muscles" - or - wrong = "muscels" and right = "mussels" - or - wrong = "muscial" and right = "musical" - or - wrong = "muscician" and right = "musician" - or - wrong = "muscicians" and right = "musicians" - or - wrong = "mutiliated" and right = "mutilated" - or - wrong = "mutiple" and right = "multiple" - or - wrong = "myraid" and right = "myriad" - or - wrong = "mysef" and right = "myself" - or - wrong = "mysogynist" and right = "misogynist" - or - wrong = "mysogyny" and right = "misogyny" - or - wrong = "mysterous" and right = "mysterious" - or - wrong = "mythraic" and right = "mithraic" - or - wrong = "naieve" and right = "naive" - or - wrong = "naploeon" and right = "napoleon" - or - wrong = "napolean" and right = "napoleon" - or - wrong = "napoleonian" and right = "napoleonic" - or - wrong = "naturaly" and right = "naturally" - or - wrong = "naturely" and right = "naturally" - or - wrong = "naturual" and right = "natural" - or - wrong = "naturually" and right = "naturally" - or - wrong = "nazereth" and right = "nazareth" - or - wrong = "neccesarily" and right = "necessarily" - or - wrong = "neccesary" and right = "necessary" - or - wrong = "neccessarily" and right = "necessarily" - or - wrong = "neccessary" and right = "necessary" - or - wrong = "neccessities" and right = "necessities" - or - wrong = "necesarily" and right = "necessarily" - or - wrong = "necesary" and right = "necessary" - or - wrong = "necessiate" and right = "necessitate" - or - wrong = "neglible" and right = "negligible" - or - wrong = "negligable" and right = "negligible" - or - wrong = "negociate" and right = "negotiate" - or - wrong = "negociation" and right = "negotiation" - or - wrong = "negociations" and right = "negotiations" - or - wrong = "negotation" and right = "negotiation" - or - wrong = "neice" and right = "nice" - or - wrong = "neice" and right = "niece" - or - wrong = "neigborhood" and right = "neighborhood" - or - wrong = "neigbour" and right = "neighbor" - or - wrong = "neigbour" and right = "neighbour" - or - wrong = "neigbourhood" and right = "neighbourhood" - or - wrong = "neigbouring" and right = "neighboring" - or - wrong = "neigbouring" and right = "neighbouring" - or - wrong = "neigbours" and right = "neighbors" - or - wrong = "neigbours" and right = "neighbours" - or - wrong = "neolitic" and right = "neolithic" - or - wrong = "nessasarily" and right = "necessarily" - or - wrong = "nessecary" and right = "necessary" - or - wrong = "nestin" and right = "nesting" - or - wrong = "neverthless" and right = "nevertheless" - or - wrong = "newletters" and right = "newsletters" - or - wrong = "nickle" and right = "nickel" - or - wrong = "nightime" and right = "nighttime" - or - wrong = "nineth" and right = "ninth" - or - wrong = "ninteenth" and right = "nineteenth" - or - wrong = "ninty" and right = "ninety" - or - wrong = "nkow" and right = "know" - or - wrong = "nkwo" and right = "know" - or - wrong = "nmae" and right = "name" - or - wrong = "noncombatents" and right = "noncombatants" - or - wrong = "nonsence" and right = "nonsense" - or - wrong = "nontheless" and right = "nonetheless" - or - wrong = "norhern" and right = "northern" - or - wrong = "northen" and right = "northern" - or - wrong = "northereastern" and right = "northeastern" - or - wrong = "notabley" and right = "notably" - or - wrong = "noteable" and right = "notable" - or - wrong = "noteably" and right = "notably" - or - wrong = "noteriety" and right = "notoriety" - or - wrong = "noth" and right = "north" - or - wrong = "nothern" and right = "northern" - or - wrong = "noticable" and right = "noticeable" - or - wrong = "noticably" and right = "noticeably" - or - wrong = "notications" and right = "notifications" - or - wrong = "noticeing" and right = "noticing" - or - wrong = "noticible" and right = "noticeable" - or - wrong = "notwhithstanding" and right = "notwithstanding" - or - wrong = "noveau" and right = "nouveau" - or - wrong = "novermber" and right = "november" - or - wrong = "nowdays" and right = "nowadays" - or - wrong = "nowe" and right = "now" - or - wrong = "nto" and right = "not" - or - wrong = "nubmer" and right = "number" - or - wrong = "nucular" and right = "nuclear" - or - wrong = "nuculear" and right = "nuclear" - or - wrong = "nuisanse" and right = "nuisance" - or - wrong = "nullabour" and right = "nullarbor" - or - wrong = "numberous" and right = "numerous" - or - wrong = "nuremburg" and right = "nuremberg" - or - wrong = "nusance" and right = "nuisance" - or - wrong = "nutritent" and right = "nutrient" - or - wrong = "nutritents" and right = "nutrients" - or - wrong = "nuturing" and right = "nurturing" - or - wrong = "obect" and right = "object" - or - wrong = "obediance" and right = "obedience" - or - wrong = "obediant" and right = "obedient" - or - wrong = "obejct" and right = "object" - or - wrong = "obession" and right = "obsession" - or - wrong = "obssessed" and right = "obsessed" - or - wrong = "obstacal" and right = "obstacle" - or - wrong = "obstancles" and right = "obstacles" - or - wrong = "obstruced" and right = "obstructed" - or - wrong = "ocasion" and right = "occasion" - or - wrong = "ocasional" and right = "occasional" - or - wrong = "ocasionally" and right = "occasionally" - or - wrong = "ocasionaly" and right = "occasionally" - or - wrong = "ocasioned" and right = "occasioned" - or - wrong = "ocasions" and right = "occasions" - or - wrong = "ocassion" and right = "occasion" - or - wrong = "ocassional" and right = "occasional" - or - wrong = "ocassionally" and right = "occasionally" - or - wrong = "ocassionaly" and right = "occasionally" - or - wrong = "ocassioned" and right = "occasioned" - or - wrong = "ocassions" and right = "occasions" - or - wrong = "occaison" and right = "occasion" - or - wrong = "occassion" and right = "occasion" - or - wrong = "occassional" and right = "occasional" - or - wrong = "occassionally" and right = "occasionally" - or - wrong = "occassionaly" and right = "occasionally" - or - wrong = "occassioned" and right = "occasioned" - or - wrong = "occassions" and right = "occasions" - or - wrong = "occationally" and right = "occasionally" - or - wrong = "occour" and right = "occur" - or - wrong = "occurance" and right = "occurrence" - or - wrong = "occurances" and right = "occurrences" - or - wrong = "occured" and right = "occurred" - or - wrong = "occurence" and right = "occurrence" - or - wrong = "occurences" and right = "occurrences" - or - wrong = "occuring" and right = "occurring" - or - wrong = "occurr" and right = "occur" - or - wrong = "occurrance" and right = "occurrence" - or - wrong = "occurrances" and right = "occurrences" - or - wrong = "octohedra" and right = "octahedra" - or - wrong = "octohedral" and right = "octahedral" - or - wrong = "octohedron" and right = "octahedron" - or - wrong = "ocuntries" and right = "countries" - or - wrong = "ocuntry" and right = "country" - or - wrong = "ocurr" and right = "occur" - or - wrong = "ocurrance" and right = "occurrence" - or - wrong = "ocurred" and right = "occurred" - or - wrong = "ocurrence" and right = "occurrence" - or - wrong = "offcers" and right = "officers" - or - wrong = "offcially" and right = "officially" - or - wrong = "offereings" and right = "offerings" - or - wrong = "offical" and right = "official" - or - wrong = "offically" and right = "officially" - or - wrong = "officals" and right = "officials" - or - wrong = "officaly" and right = "officially" - or - wrong = "officialy" and right = "officially" - or - wrong = "offred" and right = "offered" - or - wrong = "oftenly" and right = "often" - or - wrong = "oging" and right = "going" - or - wrong = "oging" and right = "ogling" - or - wrong = "oject" and right = "object" - or - wrong = "omision" and right = "omission" - or - wrong = "omited" and right = "omitted" - or - wrong = "omiting" and right = "omitting" - or - wrong = "omlette" and right = "omelette" - or - wrong = "ommision" and right = "omission" - or - wrong = "ommited" and right = "omitted" - or - wrong = "ommiting" and right = "omitting" - or - wrong = "ommitted" and right = "omitted" - or - wrong = "ommitting" and right = "omitting" - or - wrong = "omniverous" and right = "omnivorous" - or - wrong = "omniverously" and right = "omnivorously" - or - wrong = "omre" and right = "more" - or - wrong = "onot" and right = "not" - or - wrong = "onot" and right = "note" - or - wrong = "onyl" and right = "only" - or - wrong = "openess" and right = "openness" - or - wrong = "oponent" and right = "opponent" - or - wrong = "oportunity" and right = "opportunity" - or - wrong = "opose" and right = "oppose" - or - wrong = "oposite" and right = "opposite" - or - wrong = "oposition" and right = "opposition" - or - wrong = "oppenly" and right = "openly" - or - wrong = "oppinion" and right = "opinion" - or - wrong = "opponant" and right = "opponent" - or - wrong = "oppononent" and right = "opponent" - or - wrong = "oppositition" and right = "opposition" - or - wrong = "oppossed" and right = "opposed" - or - wrong = "opprotunity" and right = "opportunity" - or - wrong = "opression" and right = "oppression" - or - wrong = "opressive" and right = "oppressive" - or - wrong = "opthalmic" and right = "ophthalmic" - or - wrong = "opthalmologist" and right = "ophthalmologist" - or - wrong = "opthalmology" and right = "ophthalmology" - or - wrong = "opthamologist" and right = "ophthalmologist" - or - wrong = "optmizations" and right = "optimizations" - or - wrong = "optomism" and right = "optimism" - or - wrong = "orded" and right = "ordered" - or - wrong = "organim" and right = "organism" - or - wrong = "organistion" and right = "organisation" - or - wrong = "organiztion" and right = "organization" - or - wrong = "orgin" and right = "organ" - or - wrong = "orgin" and right = "origin" - or - wrong = "orginal" and right = "original" - or - wrong = "orginally" and right = "originally" - or - wrong = "orginize" and right = "organise" - or - wrong = "oridinarily" and right = "ordinarily" - or - wrong = "origanaly" and right = "originally" - or - wrong = "originall" and right = "original" - or - wrong = "originall" and right = "originally" - or - wrong = "originaly" and right = "originally" - or - wrong = "originially" and right = "originally" - or - wrong = "originnally" and right = "originally" - or - wrong = "origional" and right = "original" - or - wrong = "orignally" and right = "originally" - or - wrong = "orignially" and right = "originally" - or - wrong = "otehr" and right = "other" - or - wrong = "oublisher" and right = "publisher" - or - wrong = "ouevre" and right = "oeuvre" - or - wrong = "ouput" and right = "output" - or - wrong = "oustanding" and right = "outstanding" - or - wrong = "overriden" and right = "overridden" - or - wrong = "overshaddowed" and right = "overshadowed" - or - wrong = "overwelming" and right = "overwhelming" - or - wrong = "overwheliming" and right = "overwhelming" - or - wrong = "owrk" and right = "work" - or - wrong = "owudl" and right = "would" - or - wrong = "oxigen" and right = "oxygen" - or - wrong = "oximoron" and right = "oxymoron" - or - wrong = "p0enis" and right = "penis" - or - wrong = "paide" and right = "paid" - or - wrong = "paitience" and right = "patience" - or - wrong = "palce" and right = "palace" - or - wrong = "palce" and right = "place" - or - wrong = "paleolitic" and right = "paleolithic" - or - wrong = "paliamentarian" and right = "parliamentarian" - or - wrong = "palistian" and right = "palestinian" - or - wrong = "palistinian" and right = "palestinian" - or - wrong = "palistinians" and right = "palestinians" - or - wrong = "pallete" and right = "palette" - or - wrong = "pamflet" and right = "pamphlet" - or - wrong = "pamplet" and right = "pamphlet" - or - wrong = "pantomine" and right = "pantomime" - or - wrong = "papanicalou" and right = "papanicolaou" - or - wrong = "paralel" and right = "parallel" - or - wrong = "paralell" and right = "parallel" - or - wrong = "paralelly" and right = "parallelly" - or - wrong = "paralely" and right = "parallelly" - or - wrong = "parallely" and right = "parallelly" - or - wrong = "paramater" and right = "parameter" - or - wrong = "paramters" and right = "parameters" - or - wrong = "parametarized" and right = "parameterized" - or - wrong = "paranthesis" and right = "parenthesis" - or - wrong = "paraphenalia" and right = "paraphernalia" - or - wrong = "parellels" and right = "parallels" - or - wrong = "parisitic" and right = "parasitic" - or - wrong = "parituclar" and right = "particular" - or - wrong = "parliment" and right = "parliament" - or - wrong = "parrakeets" and right = "parakeets" - or - wrong = "parralel" and right = "parallel" - or - wrong = "parrallel" and right = "parallel" - or - wrong = "parrallell" and right = "parallel" - or - wrong = "parrallelly" and right = "parallelly" - or - wrong = "parrallely" and right = "parallelly" - or - wrong = "partialy" and right = "partially" - or - wrong = "particually" and right = "particularly" - or - wrong = "particualr" and right = "particular" - or - wrong = "particuarly" and right = "particularly" - or - wrong = "particularily" and right = "particularly" - or - wrong = "particulary" and right = "particularly" - or - wrong = "pary" and right = "party" - or - wrong = "pased" and right = "passed" - or - wrong = "pasengers" and right = "passengers" - or - wrong = "passerbys" and right = "passersby" - or - wrong = "pasttime" and right = "pastime" - or - wrong = "pastural" and right = "pastoral" - or - wrong = "paticular" and right = "particular" - or - wrong = "pattented" and right = "patented" - or - wrong = "pavillion" and right = "pavilion" - or - wrong = "payed" and right = "paid" - or - wrong = "pblisher" and right = "publisher" - or - wrong = "pbulisher" and right = "publisher" - or - wrong = "peageant" and right = "pageant" - or - wrong = "peaple" and right = "people" - or - wrong = "peaples" and right = "peoples" - or - wrong = "peculure" and right = "peculiar" - or - wrong = "pedestrain" and right = "pedestrian" - or - wrong = "peformed" and right = "performed" - or - wrong = "peice" and right = "piece" - or - wrong = "peloponnes" and right = "peloponnesus" - or - wrong = "penatly" and right = "penalty" - or - wrong = "penerator" and right = "penetrator" - or - wrong = "penisula" and right = "peninsula" - or - wrong = "penisular" and right = "peninsular" - or - wrong = "penninsula" and right = "peninsula" - or - wrong = "penninsular" and right = "peninsular" - or - wrong = "pennisula" and right = "peninsula" - or - wrong = "pennyslvania" and right = "pennsylvania" - or - wrong = "pensinula" and right = "peninsula" - or - wrong = "pensle" and right = "pencil" - or - wrong = "peom" and right = "poem" - or - wrong = "peoms" and right = "poems" - or - wrong = "peopel" and right = "people" - or - wrong = "peopels" and right = "peoples" - or - wrong = "peotry" and right = "poetry" - or - wrong = "perade" and right = "parade" - or - wrong = "percepted" and right = "perceived" - or - wrong = "percieve" and right = "perceive" - or - wrong = "percieved" and right = "perceived" - or - wrong = "perenially" and right = "perennially" - or - wrong = "peretrator" and right = "perpetrator" - or - wrong = "perfomance" and right = "performance" - or - wrong = "perfomers" and right = "performers" - or - wrong = "performence" and right = "performance" - or - wrong = "performes" and right = "performed" - or - wrong = "performes" and right = "performs" - or - wrong = "perhasp" and right = "perhaps" - or - wrong = "perheaps" and right = "perhaps" - or - wrong = "perhpas" and right = "perhaps" - or - wrong = "peripathetic" and right = "peripatetic" - or - wrong = "peristent" and right = "persistent" - or - wrong = "perjery" and right = "perjury" - or - wrong = "perjorative" and right = "pejorative" - or - wrong = "permanant" and right = "permanent" - or - wrong = "permenant" and right = "permanent" - or - wrong = "permenantly" and right = "permanently" - or - wrong = "permissable" and right = "permissible" - or - wrong = "perogative" and right = "prerogative" - or - wrong = "peronal" and right = "personal" - or - wrong = "perosnality" and right = "personality" - or - wrong = "perpertrated" and right = "perpetrated" - or - wrong = "perphas" and right = "perhaps" - or - wrong = "perpindicular" and right = "perpendicular" - or - wrong = "persan" and right = "person" - or - wrong = "perseverence" and right = "perseverance" - or - wrong = "persistance" and right = "persistence" - or - wrong = "persistant" and right = "persistent" - or - wrong = "personel" and right = "personal" - or - wrong = "personel" and right = "personnel" - or - wrong = "personell" and right = "personnel" - or - wrong = "personnell" and right = "personnel" - or - wrong = "persuded" and right = "persuaded" - or - wrong = "persue" and right = "pursue" - or - wrong = "persued" and right = "pursued" - or - wrong = "persuing" and right = "pursuing" - or - wrong = "persuit" and right = "pursuit" - or - wrong = "persuits" and right = "pursuits" - or - wrong = "pertubation" and right = "perturbation" - or - wrong = "pertubations" and right = "perturbations" - or - wrong = "pessiary" and right = "pessary" - or - wrong = "petetion" and right = "petition" - or - wrong = "pharoah" and right = "pharaoh" - or - wrong = "phenomenom" and right = "phenomenon" - or - wrong = "phenomenonal" and right = "phenomenal" - or - wrong = "phenomenonly" and right = "phenomenally" - or - wrong = "phenomonenon" and right = "phenomenon" - or - wrong = "phenomonon" and right = "phenomenon" - or - wrong = "phenonmena" and right = "phenomena" - or - wrong = "philipines" and right = "philippines" - or - wrong = "philisopher" and right = "philosopher" - or - wrong = "philisophical" and right = "philosophical" - or - wrong = "philisophy" and right = "philosophy" - or - wrong = "phillipine" and right = "philippine" - or - wrong = "phillipines" and right = "philippines" - or - wrong = "phillippines" and right = "philippines" - or - wrong = "phillosophically" and right = "philosophically" - or - wrong = "philospher" and right = "philosopher" - or - wrong = "philosphies" and right = "philosophies" - or - wrong = "philosphy" and right = "philosophy" - or - wrong = "phonecian" and right = "phoenecian" - or - wrong = "phongraph" and right = "phonograph" - or - wrong = "phylosophical" and right = "philosophical" - or - wrong = "physicaly" and right = "physically" - or - wrong = "piblisher" and right = "publisher" - or - wrong = "pich" and right = "pitch" - or - wrong = "pilgrimmage" and right = "pilgrimage" - or - wrong = "pilgrimmages" and right = "pilgrimages" - or - wrong = "pinapple" and right = "pineapple" - or - wrong = "pinnaple" and right = "pineapple" - or - wrong = "pinoneered" and right = "pioneered" - or - wrong = "plagarism" and right = "plagiarism" - or - wrong = "planation" and right = "plantation" - or - wrong = "planed" and right = "planned" - or - wrong = "plantiff" and right = "plaintiff" - or - wrong = "plateu" and right = "plateau" - or - wrong = "plausable" and right = "plausible" - or - wrong = "playright" and right = "playwright" - or - wrong = "playwrite" and right = "playwright" - or - wrong = "playwrites" and right = "playwrights" - or - wrong = "pleasent" and right = "pleasant" - or - wrong = "plebicite" and right = "plebiscite" - or - wrong = "plesant" and right = "pleasant" - or - wrong = "poenis" and right = "penis" - or - wrong = "poeoples" and right = "peoples" - or - wrong = "poety" and right = "poetry" - or - wrong = "poisin" and right = "poison" - or - wrong = "polical" and right = "political" - or - wrong = "polinator" and right = "pollinator" - or - wrong = "polinators" and right = "pollinators" - or - wrong = "politican" and right = "politician" - or - wrong = "politicans" and right = "politicians" - or - wrong = "poltical" and right = "political" - or - wrong = "polute" and right = "pollute" - or - wrong = "poluted" and right = "polluted" - or - wrong = "polutes" and right = "pollutes" - or - wrong = "poluting" and right = "polluting" - or - wrong = "polution" and right = "pollution" - or - wrong = "polyphonyic" and right = "polyphonic" - or - wrong = "polysaccaride" and right = "polysaccharide" - or - wrong = "polysaccharid" and right = "polysaccharide" - or - wrong = "pomegranite" and right = "pomegranate" - or - wrong = "pomotion" and right = "promotion" - or - wrong = "poportional" and right = "proportional" - or - wrong = "popoulation" and right = "population" - or - wrong = "popularaty" and right = "popularity" - or - wrong = "populare" and right = "popular" - or - wrong = "populer" and right = "popular" - or - wrong = "porshan" and right = "portion" - or - wrong = "porshon" and right = "portion" - or - wrong = "portait" and right = "portrait" - or - wrong = "portayed" and right = "portrayed" - or - wrong = "portraing" and right = "portraying" - or - wrong = "portugese" and right = "portuguese" - or - wrong = "portuguease" and right = "portuguese" - or - wrong = "portugues" and right = "portuguese" - or - wrong = "posess" and right = "possess" - or - wrong = "posessed" and right = "possessed" - or - wrong = "posesses" and right = "possesses" - or - wrong = "posessing" and right = "possessing" - or - wrong = "posession" and right = "possession" - or - wrong = "posessions" and right = "possessions" - or - wrong = "posion" and right = "poison" - or - wrong = "positon" and right = "position" - or - wrong = "positon" and right = "positron" - or - wrong = "possable" and right = "possible" - or - wrong = "possably" and right = "possibly" - or - wrong = "posseses" and right = "possesses" - or - wrong = "possesing" and right = "possessing" - or - wrong = "possesion" and right = "possession" - or - wrong = "possessess" and right = "possesses" - or - wrong = "possibile" and right = "possible" - or - wrong = "possibilty" and right = "possibility" - or - wrong = "possiblility" and right = "possibility" - or - wrong = "possiblilty" and right = "possibility" - or - wrong = "possiblities" and right = "possibilities" - or - wrong = "possiblity" and right = "possibility" - or - wrong = "possition" and right = "position" - or - wrong = "postdam" and right = "potsdam" - or - wrong = "posthomous" and right = "posthumous" - or - wrong = "postion" and right = "position" - or - wrong = "postive" and right = "positive" - or - wrong = "potatos" and right = "potatoes" - or - wrong = "potrait" and right = "portrait" - or - wrong = "potrayed" and right = "portrayed" - or - wrong = "poulations" and right = "populations" - or - wrong = "poverful" and right = "powerful" - or - wrong = "poweful" and right = "powerful" - or - wrong = "powerfull" and right = "powerful" - or - wrong = "ppublisher" and right = "publisher" - or - wrong = "practial" and right = "practical" - or - wrong = "practially" and right = "practically" - or - wrong = "practicaly" and right = "practically" - or - wrong = "practicioner" and right = "practitioner" - or - wrong = "practicioners" and right = "practitioners" - or - wrong = "practicly" and right = "practically" - or - wrong = "practioner" and right = "practitioner" - or - wrong = "practioners" and right = "practitioners" - or - wrong = "prairy" and right = "prairie" - or - wrong = "prarie" and right = "prairie" - or - wrong = "praries" and right = "prairies" - or - wrong = "pratice" and right = "practice" - or - wrong = "preample" and right = "preamble" - or - wrong = "precedessor" and right = "predecessor" - or - wrong = "preceed" and right = "precede" - or - wrong = "preceeded" and right = "preceded" - or - wrong = "preceeding" and right = "preceding" - or - wrong = "preceeds" and right = "precedes" - or - wrong = "precendence" and right = "precedence" - or - wrong = "precentage" and right = "percentage" - or - wrong = "precice" and right = "precise" - or - wrong = "precisly" and right = "precisely" - or - wrong = "precurser" and right = "precursor" - or - wrong = "predecesors" and right = "predecessors" - or - wrong = "predicatble" and right = "predictable" - or - wrong = "predicitons" and right = "predictions" - or - wrong = "predomiantly" and right = "predominately" - or - wrong = "prefered" and right = "preferred" - or - wrong = "prefering" and right = "preferring" - or - wrong = "preferrably" and right = "preferably" - or - wrong = "pregancies" and right = "pregnancies" - or - wrong = "preiod" and right = "period" - or - wrong = "preliferation" and right = "proliferation" - or - wrong = "premeire" and right = "premiere" - or - wrong = "premeired" and right = "premiered" - or - wrong = "premillenial" and right = "premillennial" - or - wrong = "preminence" and right = "preeminence" - or - wrong = "premission" and right = "permission" - or - wrong = "premonasterians" and right = "premonstratensians" - or - wrong = "preocupation" and right = "preoccupation" - or - wrong = "prepair" and right = "prepare" - or - wrong = "prepartion" and right = "preparation" - or - wrong = "prepatory" and right = "preparatory" - or - wrong = "preperation" and right = "preparation" - or - wrong = "preperations" and right = "preparations" - or - wrong = "preriod" and right = "period" - or - wrong = "presedential" and right = "presidential" - or - wrong = "presense" and right = "presence" - or - wrong = "presidenital" and right = "presidential" - or - wrong = "presidental" and right = "presidential" - or - wrong = "presitgious" and right = "prestigious" - or - wrong = "prespective" and right = "perspective" - or - wrong = "prestigeous" and right = "prestigious" - or - wrong = "prestigous" and right = "prestigious" - or - wrong = "presumabely" and right = "presumably" - or - wrong = "presumibly" and right = "presumably" - or - wrong = "pretection" and right = "protection" - or - wrong = "prevelant" and right = "prevalent" - or - wrong = "preverse" and right = "perverse" - or - wrong = "previvous" and right = "previous" - or - wrong = "pricipal" and right = "principal" - or - wrong = "priciple" and right = "principle" - or - wrong = "priestood" and right = "priesthood" - or - wrong = "primarly" and right = "primarily" - or - wrong = "primative" and right = "primitive" - or - wrong = "primatively" and right = "primitively" - or - wrong = "primatives" and right = "primitives" - or - wrong = "primordal" and right = "primordial" - or - wrong = "principaly" and right = "principality" - or - wrong = "principial" and right = "principal" - or - wrong = "principlaity" and right = "principality" - or - wrong = "principly" and right = "principally" - or - wrong = "prinicipal" and right = "principal" - or - wrong = "privalege" and right = "privilege" - or - wrong = "privaleges" and right = "privileges" - or - wrong = "priveledges" and right = "privileges" - or - wrong = "privelege" and right = "privilege" - or - wrong = "priveleged" and right = "privileged" - or - wrong = "priveleges" and right = "privileges" - or - wrong = "privelige" and right = "privilege" - or - wrong = "priveliged" and right = "privileged" - or - wrong = "priveliges" and right = "privileges" - or - wrong = "privelleges" and right = "privileges" - or - wrong = "privilage" and right = "privilege" - or - wrong = "priviledge" and right = "privilege" - or - wrong = "priviledges" and right = "privileges" - or - wrong = "privledge" and right = "privilege" - or - wrong = "privte" and right = "private" - or - wrong = "probabilaty" and right = "probability" - or - wrong = "probablistic" and right = "probabilistic" - or - wrong = "probablly" and right = "probably" - or - wrong = "probalibity" and right = "probability" - or - wrong = "probaly" and right = "probably" - or - wrong = "probelm" and right = "problem" - or - wrong = "proccess" and right = "process" - or - wrong = "proccessing" and right = "processing" - or - wrong = "procede" and right = "precede" - or - wrong = "procede" and right = "proceed" - or - wrong = "proceded" and right = "preceded" - or - wrong = "proceded" and right = "proceeded" - or - wrong = "procedes" and right = "precedes" - or - wrong = "procedes" and right = "proceeds" - or - wrong = "procedger" and right = "procedure" - or - wrong = "proceding" and right = "preceding" - or - wrong = "proceding" and right = "proceeding" - or - wrong = "procedings" and right = "proceedings" - or - wrong = "proceedure" and right = "procedure" - or - wrong = "proces" and right = "process" - or - wrong = "procesed" and right = "processed" - or - wrong = "processer" and right = "processor" - or - wrong = "proclaimation" and right = "proclamation" - or - wrong = "proclamed" and right = "proclaimed" - or - wrong = "proclaming" and right = "proclaiming" - or - wrong = "proclomation" and right = "proclamation" - or - wrong = "profesion" and right = "profession" - or - wrong = "profesion" and right = "profusion" - or - wrong = "profesor" and right = "professor" - or - wrong = "professer" and right = "professor" - or - wrong = "proffesed" and right = "professed" - or - wrong = "proffesion" and right = "profession" - or - wrong = "proffesional" and right = "professional" - or - wrong = "proffesor" and right = "professor" - or - wrong = "profilic" and right = "prolific" - or - wrong = "progessed" and right = "progressed" - or - wrong = "progidy" and right = "prodigy" - or - wrong = "programable" and right = "programmable" - or - wrong = "progrom" and right = "pogrom" - or - wrong = "progrom" and right = "program" - or - wrong = "progroms" and right = "pogroms" - or - wrong = "progroms" and right = "programs" - or - wrong = "prohabition" and right = "prohibition" - or - wrong = "prologomena" and right = "prolegomena" - or - wrong = "prominance" and right = "prominence" - or - wrong = "prominant" and right = "prominent" - or - wrong = "prominantly" and right = "prominently" - or - wrong = "prominately" and right = "predominately" - or - wrong = "prominately" and right = "prominently" - or - wrong = "promiscous" and right = "promiscuous" - or - wrong = "promotted" and right = "promoted" - or - wrong = "pronomial" and right = "pronominal" - or - wrong = "pronouced" and right = "pronounced" - or - wrong = "pronounched" and right = "pronounced" - or - wrong = "pronounciation" and right = "pronunciation" - or - wrong = "proove" and right = "prove" - or - wrong = "prooved" and right = "proved" - or - wrong = "prophacy" and right = "prophecy" - or - wrong = "propietary" and right = "proprietary" - or - wrong = "propmted" and right = "prompted" - or - wrong = "propoganda" and right = "propaganda" - or - wrong = "propogate" and right = "propagate" - or - wrong = "propogates" and right = "propagates" - or - wrong = "propogation" and right = "propagation" - or - wrong = "propostion" and right = "proposition" - or - wrong = "propotions" and right = "proportions" - or - wrong = "propper" and right = "proper" - or - wrong = "propperly" and right = "properly" - or - wrong = "proprietory" and right = "proprietary" - or - wrong = "proseletyzing" and right = "proselytizing" - or - wrong = "protaganist" and right = "protagonist" - or - wrong = "protaganists" and right = "protagonists" - or - wrong = "protocal" and right = "protocol" - or - wrong = "protoganist" and right = "protagonist" - or - wrong = "prototpe" and right = "prototype" - or - wrong = "protoype" and right = "prototype" - or - wrong = "protrayed" and right = "portrayed" - or - wrong = "protruberance" and right = "protuberance" - or - wrong = "protruberances" and right = "protuberances" - or - wrong = "prouncements" and right = "pronouncements" - or - wrong = "provacative" and right = "provocative" - or - wrong = "provded" and right = "provided" - or - wrong = "provicial" and right = "provincial" - or - wrong = "provinicial" and right = "provincial" - or - wrong = "provisiosn" and right = "provision" - or - wrong = "provisonal" and right = "provisional" - or - wrong = "proximty" and right = "proximity" - or - wrong = "pseudononymous" and right = "pseudonymous" - or - wrong = "pseudonyn" and right = "pseudonym" - or - wrong = "psuedo" and right = "pseudo" - or - wrong = "psycology" and right = "psychology" - or - wrong = "psyhic" and right = "psychic" - or - wrong = "pubilsher" and right = "publisher" - or - wrong = "pubisher" and right = "publisher" - or - wrong = "publiaher" and right = "publisher" - or - wrong = "publically" and right = "publicly" - or - wrong = "publicaly" and right = "publicly" - or - wrong = "publicher" and right = "publisher" - or - wrong = "publihser" and right = "publisher" - or - wrong = "publisehr" and right = "publisher" - or - wrong = "publiser" and right = "publisher" - or - wrong = "publisger" and right = "publisher" - or - wrong = "publisheed" and right = "published" - or - wrong = "publisherr" and right = "publisher" - or - wrong = "publishher" and right = "publisher" - or - wrong = "publishor" and right = "publisher" - or - wrong = "publishre" and right = "publisher" - or - wrong = "publissher" and right = "publisher" - or - wrong = "publlisher" and right = "publisher" - or - wrong = "publsiher" and right = "publisher" - or - wrong = "publusher" and right = "publisher" - or - wrong = "puchasing" and right = "purchasing" - or - wrong = "pucini" and right = "puccini" - or - wrong = "pulisher" and right = "publisher" - or - wrong = "pumkin" and right = "pumpkin" - or - wrong = "puplisher" and right = "publisher" - or - wrong = "puritannical" and right = "puritanical" - or - wrong = "purposedly" and right = "purposely" - or - wrong = "purpotedly" and right = "purportedly" - or - wrong = "pursuade" and right = "persuade" - or - wrong = "pursuaded" and right = "persuaded" - or - wrong = "pursuades" and right = "persuades" - or - wrong = "pususading" and right = "persuading" - or - wrong = "puting" and right = "putting" - or - wrong = "pwoer" and right = "power" - or - wrong = "pyscic" and right = "psychic" - or - wrong = "qtuie" and right = "quiet" - or - wrong = "qtuie" and right = "quite" - or - wrong = "quantaty" and right = "quantity" - or - wrong = "quantitiy" and right = "quantity" - or - wrong = "quarantaine" and right = "quarantine" - or - wrong = "queenland" and right = "queensland" - or - wrong = "questonable" and right = "questionable" - or - wrong = "quicklyu" and right = "quickly" - or - wrong = "quinessential" and right = "quintessential" - or - wrong = "quitted" and right = "quit" - or - wrong = "quizes" and right = "quizzes" - or - wrong = "qutie" and right = "quiet" - or - wrong = "qutie" and right = "quite" - or - wrong = "rabinnical" and right = "rabbinical" - or - wrong = "racaus" and right = "raucous" - or - wrong = "radiactive" and right = "radioactive" - or - wrong = "radify" and right = "ratify" - or - wrong = "raelly" and right = "really" - or - wrong = "rarified" and right = "rarefied" - or - wrong = "reaccurring" and right = "recurring" - or - wrong = "reacing" and right = "reaching" - or - wrong = "reacll" and right = "recall" - or - wrong = "readmition" and right = "readmission" - or - wrong = "realitvely" and right = "relatively" - or - wrong = "realsitic" and right = "realistic" - or - wrong = "realtions" and right = "relations" - or - wrong = "realy" and right = "really" - or - wrong = "realyl" and right = "really" - or - wrong = "reasearch" and right = "research" - or - wrong = "rebiulding" and right = "rebuilding" - or - wrong = "rebllions" and right = "rebellions" - or - wrong = "rebounce" and right = "rebound" - or - wrong = "reccomend" and right = "recommend" - or - wrong = "reccomendations" and right = "recommendations" - or - wrong = "reccomended" and right = "recommended" - or - wrong = "reccomending" and right = "recommending" - or - wrong = "reccommend" and right = "recommend" - or - wrong = "reccommended" and right = "recommended" - or - wrong = "reccommending" and right = "recommending" - or - wrong = "reccuring" and right = "recurring" - or - wrong = "receeded" and right = "receded" - or - wrong = "receeding" and right = "receding" - or - wrong = "recepient" and right = "recipient" - or - wrong = "recepients" and right = "recipients" - or - wrong = "receving" and right = "receiving" - or - wrong = "rechargable" and right = "rechargeable" - or - wrong = "reched" and right = "reached" - or - wrong = "recide" and right = "reside" - or - wrong = "recided" and right = "resided" - or - wrong = "recident" and right = "resident" - or - wrong = "recidents" and right = "residents" - or - wrong = "reciding" and right = "residing" - or - wrong = "reciepents" and right = "recipients" - or - wrong = "reciept" and right = "receipt" - or - wrong = "recieve" and right = "receive" - or - wrong = "recieved" and right = "received" - or - wrong = "reciever" and right = "receiver" - or - wrong = "recievers" and right = "receivers" - or - wrong = "recieves" and right = "receives" - or - wrong = "recieving" and right = "receiving" - or - wrong = "recipiant" and right = "recipient" - or - wrong = "recipiants" and right = "recipients" - or - wrong = "recived" and right = "received" - or - wrong = "recivership" and right = "receivership" - or - wrong = "recogise" and right = "recognise" - or - wrong = "recogize" and right = "recognize" - or - wrong = "recomend" and right = "recommend" - or - wrong = "recomended" and right = "recommended" - or - wrong = "recomending" and right = "recommending" - or - wrong = "recomends" and right = "recommends" - or - wrong = "recommedations" and right = "recommendations" - or - wrong = "recompence" and right = "recompense" - or - wrong = "reconaissance" and right = "reconnaissance" - or - wrong = "reconcilation" and right = "reconciliation" - or - wrong = "reconized" and right = "recognized" - or - wrong = "reconnaisance" and right = "reconnaissance" - or - wrong = "reconnaissence" and right = "reconnaissance" - or - wrong = "recontructed" and right = "reconstructed" - or - wrong = "recquired" and right = "required" - or - wrong = "recrational" and right = "recreational" - or - wrong = "recrod" and right = "record" - or - wrong = "recuiting" and right = "recruiting" - or - wrong = "recuring" and right = "recurring" - or - wrong = "recurrance" and right = "recurrence" - or - wrong = "rediculous" and right = "ridiculous" - or - wrong = "reedeming" and right = "redeeming" - or - wrong = "reenforced" and right = "reinforced" - or - wrong = "refect" and right = "reflect" - or - wrong = "refedendum" and right = "referendum" - or - wrong = "referal" and right = "referral" - or - wrong = "referece" and right = "reference" - or - wrong = "refereces" and right = "references" - or - wrong = "refered" and right = "referred" - or - wrong = "referemce" and right = "reference" - or - wrong = "referemces" and right = "references" - or - wrong = "referencs" and right = "references" - or - wrong = "referenece" and right = "reference" - or - wrong = "refereneced" and right = "referenced" - or - wrong = "refereneces" and right = "references" - or - wrong = "referiang" and right = "referring" - or - wrong = "refering" and right = "referring" - or - wrong = "refernce" and right = "reference" - or - wrong = "refernce" and right = "references" - or - wrong = "refernces" and right = "references" - or - wrong = "referrence" and right = "reference" - or - wrong = "referrences" and right = "references" - or - wrong = "referrs" and right = "refers" - or - wrong = "reffered" and right = "referred" - or - wrong = "refference" and right = "reference" - or - wrong = "reffering" and right = "referring" - or - wrong = "refrence" and right = "reference" - or - wrong = "refrences" and right = "references" - or - wrong = "refrers" and right = "refers" - or - wrong = "refridgeration" and right = "refrigeration" - or - wrong = "refridgerator" and right = "refrigerator" - or - wrong = "refromist" and right = "reformist" - or - wrong = "refusla" and right = "refusal" - or - wrong = "regardes" and right = "regards" - or - wrong = "regluar" and right = "regular" - or - wrong = "reguarly" and right = "regularly" - or - wrong = "regulaion" and right = "regulation" - or - wrong = "regulaotrs" and right = "regulators" - or - wrong = "regularily" and right = "regularly" - or - wrong = "rehersal" and right = "rehearsal" - or - wrong = "reicarnation" and right = "reincarnation" - or - wrong = "reigining" and right = "reigning" - or - wrong = "reknown" and right = "renown" - or - wrong = "reknowned" and right = "renowned" - or - wrong = "rela" and right = "real" - or - wrong = "relaly" and right = "really" - or - wrong = "relatiopnship" and right = "relationship" - or - wrong = "relativly" and right = "relatively" - or - wrong = "relected" and right = "reelected" - or - wrong = "releive" and right = "relieve" - or - wrong = "releived" and right = "relieved" - or - wrong = "releiver" and right = "reliever" - or - wrong = "releses" and right = "releases" - or - wrong = "relevence" and right = "relevance" - or - wrong = "relevent" and right = "relevant" - or - wrong = "reliablity" and right = "reliability" - or - wrong = "relient" and right = "reliant" - or - wrong = "religeous" and right = "religious" - or - wrong = "religous" and right = "religious" - or - wrong = "religously" and right = "religiously" - or - wrong = "relinqushment" and right = "relinquishment" - or - wrong = "relitavely" and right = "relatively" - or - wrong = "relized" and right = "realised" - or - wrong = "relized" and right = "realized" - or - wrong = "relpacement" and right = "replacement" - or - wrong = "remaing" and right = "remaining" - or - wrong = "remeber" and right = "remember" - or - wrong = "rememberable" and right = "memorable" - or - wrong = "rememberance" and right = "remembrance" - or - wrong = "remembrence" and right = "remembrance" - or - wrong = "remenant" and right = "remnant" - or - wrong = "remenicent" and right = "reminiscent" - or - wrong = "reminent" and right = "remnant" - or - wrong = "reminescent" and right = "reminiscent" - or - wrong = "reminscent" and right = "reminiscent" - or - wrong = "reminsicent" and right = "reminiscent" - or - wrong = "rendevous" and right = "rendezvous" - or - wrong = "rendezous" and right = "rendezvous" - or - wrong = "renedered" and right = "rende" - or - wrong = "renewl" and right = "renewal" - or - wrong = "rennovate" and right = "renovate" - or - wrong = "rennovated" and right = "renovated" - or - wrong = "rennovating" and right = "renovating" - or - wrong = "rennovation" and right = "renovation" - or - wrong = "rentors" and right = "renters" - or - wrong = "reoccurrence" and right = "recurrence" - or - wrong = "reorganision" and right = "reorganisation" - or - wrong = "repatition" and right = "repartition" - or - wrong = "repatition" and right = "repetition" - or - wrong = "repblic" and right = "republic" - or - wrong = "repblican" and right = "republican" - or - wrong = "repblicans" and right = "republicans" - or - wrong = "repblics" and right = "republics" - or - wrong = "repectively" and right = "respectively" - or - wrong = "repeition" and right = "repetition" - or - wrong = "repentence" and right = "repentance" - or - wrong = "repentent" and right = "repentant" - or - wrong = "repeteadly" and right = "repeatedly" - or - wrong = "repetion" and right = "repetition" - or - wrong = "repid" and right = "rapid" - or - wrong = "reponse" and right = "response" - or - wrong = "reponsible" and right = "responsible" - or - wrong = "reportadly" and right = "reportedly" - or - wrong = "represantative" and right = "representative" - or - wrong = "representive" and right = "representative" - or - wrong = "representives" and right = "representatives" - or - wrong = "reproducable" and right = "reproducible" - or - wrong = "reprtoire" and right = "repertoire" - or - wrong = "repsectively" and right = "respectively" - or - wrong = "reptition" and right = "repetition" - or - wrong = "repubic" and right = "republic" - or - wrong = "repubican" and right = "republican" - or - wrong = "repubicans" and right = "republicans" - or - wrong = "repubics" and right = "republics" - or - wrong = "republi" and right = "republic" - or - wrong = "republian" and right = "republican" - or - wrong = "republians" and right = "republicans" - or - wrong = "republis" and right = "republics" - or - wrong = "repulic" and right = "republic" - or - wrong = "repulican" and right = "republican" - or - wrong = "repulicans" and right = "republicans" - or - wrong = "repulics" and right = "republics" - or - wrong = "requirment" and right = "requirement" - or - wrong = "requred" and right = "required" - or - wrong = "resaurant" and right = "restaurant" - or - wrong = "resembelance" and right = "resemblance" - or - wrong = "resembes" and right = "resembles" - or - wrong = "resemblence" and right = "resemblance" - or - wrong = "resevoir" and right = "reservoir" - or - wrong = "residental" and right = "residential" - or - wrong = "resignement" and right = "resignment" - or - wrong = "resistable" and right = "resistible" - or - wrong = "resistence" and right = "resistance" - or - wrong = "resistent" and right = "resistant" - or - wrong = "respectivly" and right = "respectively" - or - wrong = "responce" and right = "response" - or - wrong = "responibilities" and right = "responsibilities" - or - wrong = "responisble" and right = "responsible" - or - wrong = "responnsibilty" and right = "responsibility" - or - wrong = "responsability" and right = "responsibility" - or - wrong = "responsibile" and right = "responsible" - or - wrong = "responsibilites" and right = "responsibilities" - or - wrong = "responsiblities" and right = "responsibilities" - or - wrong = "responsiblity" and right = "responsibility" - or - wrong = "ressemblance" and right = "resemblance" - or - wrong = "ressemble" and right = "resemble" - or - wrong = "ressembled" and right = "resembled" - or - wrong = "ressemblence" and right = "resemblance" - or - wrong = "ressembling" and right = "resembling" - or - wrong = "resssurecting" and right = "resurrecting" - or - wrong = "ressurect" and right = "resurrect" - or - wrong = "ressurected" and right = "resurrected" - or - wrong = "ressurection" and right = "resurrection" - or - wrong = "ressurrection" and right = "resurrection" - or - wrong = "restarant" and right = "restaurant" - or - wrong = "restarants" and right = "restaurants" - or - wrong = "restaraunt" and right = "restaurant" - or - wrong = "restaraunteur" and right = "restaurateur" - or - wrong = "restaraunteurs" and right = "restaurateurs" - or - wrong = "restaraunts" and right = "restaurants" - or - wrong = "restauranteurs" and right = "restaurateurs" - or - wrong = "restauration" and right = "restoration" - or - wrong = "restauraunt" and right = "restaurant" - or - wrong = "resteraunt" and right = "restaurant" - or - wrong = "resteraunts" and right = "restaurants" - or - wrong = "resticted" and right = "restricted" - or - wrong = "restraunt" and right = "restaurant" - or - wrong = "restraunt" and right = "restraint" - or - wrong = "resturant" and right = "restaurant" - or - wrong = "resturants" and right = "restaurants" - or - wrong = "resturaunt" and right = "restaurant" - or - wrong = "resturaunts" and right = "restaurants" - or - wrong = "resurecting" and right = "resurrecting" - or - wrong = "retalitated" and right = "retaliated" - or - wrong = "retalitation" and right = "retaliation" - or - wrong = "retreive" and right = "retrieve" - or - wrong = "retrive" and right = "retrieve" - or - wrong = "returnd" and right = "returned" - or - wrong = "revaluated" and right = "reevaluated" - or - wrong = "reveiw" and right = "review" - or - wrong = "reveral" and right = "reversal" - or - wrong = "reversable" and right = "reversible" - or - wrong = "revolutionar" and right = "revolutionary" - or - wrong = "rewitten" and right = "rewritten" - or - wrong = "rewriet" and right = "rewrite" - or - wrong = "rference" and right = "reference" - or - wrong = "rferences" and right = "references" - or - wrong = "rhymme" and right = "rhyme" - or - wrong = "rhythem" and right = "rhythm" - or - wrong = "rhythim" and right = "rhythm" - or - wrong = "rhytmic" and right = "rhythmic" - or - wrong = "rigeur" and right = "rigor" - or - wrong = "rigeur" and right = "rigour" - or - wrong = "rigeur" and right = "rigueur" - or - wrong = "rigourous" and right = "rigorous" - or - wrong = "rininging" and right = "ringing" - or - wrong = "rised" and right = "raised" - or - wrong = "rised" and right = "rose" - or - wrong = "rockerfeller" and right = "rockefeller" - or - wrong = "rococco" and right = "rococo" - or - wrong = "rocord" and right = "record" - or - wrong = "roomate" and right = "roommate" - or - wrong = "rougly" and right = "roughly" - or - wrong = "rucuperate" and right = "recuperate" - or - wrong = "rudimentatry" and right = "rudimentary" - or - wrong = "rulle" and right = "rule" - or - wrong = "runing" and right = "running" - or - wrong = "runnung" and right = "running" - or - wrong = "russina" and right = "russian" - or - wrong = "russion" and right = "russian" - or - wrong = "rwite" and right = "write" - or - wrong = "rythem" and right = "rhythm" - or - wrong = "rythim" and right = "rhythm" - or - wrong = "rythm" and right = "rhythm" - or - wrong = "rythmic" and right = "rhythmic" - or - wrong = "rythyms" and right = "rhythms" - or - wrong = "sacrafice" and right = "sacrifice" - or - wrong = "sacreligious" and right = "sacrilegious" - or - wrong = "sacremento" and right = "sacramento" - or - wrong = "sacrifical" and right = "sacrificial" - or - wrong = "saftey" and right = "safety" - or - wrong = "safty" and right = "safety" - or - wrong = "salery" and right = "salary" - or - wrong = "sanctionning" and right = "sanctioning" - or - wrong = "sandwhich" and right = "sandwich" - or - wrong = "sanhedrim" and right = "sanhedrin" - or - wrong = "santioned" and right = "sanctioned" - or - wrong = "sargant" and right = "sergeant" - or - wrong = "sargeant" and right = "sergeant" - or - wrong = "sasy" and right = "sassy" - or - wrong = "sasy" and right = "says" - or - wrong = "satelite" and right = "satellite" - or - wrong = "satelites" and right = "satellites" - or - wrong = "saterday" and right = "saturday" - or - wrong = "saterdays" and right = "saturdays" - or - wrong = "satisfactority" and right = "satisfactorily" - or - wrong = "satric" and right = "satiric" - or - wrong = "satrical" and right = "satirical" - or - wrong = "satrically" and right = "satirically" - or - wrong = "sattelite" and right = "satellite" - or - wrong = "sattelites" and right = "satellites" - or - wrong = "saught" and right = "sought" - or - wrong = "saveing" and right = "saving" - or - wrong = "saxaphone" and right = "saxophone" - or - wrong = "scaleable" and right = "scalable" - or - wrong = "scandanavia" and right = "scandinavia" - or - wrong = "scaricity" and right = "scarcity" - or - wrong = "scavanged" and right = "scavenged" - or - wrong = "schedual" and right = "schedule" - or - wrong = "scholarhip" and right = "scholarship" - or - wrong = "scholarstic" and right = "scholarly" - or - wrong = "scholarstic" and right = "scholastic" - or - wrong = "scientfic" and right = "scientific" - or - wrong = "scientifc" and right = "scientific" - or - wrong = "scientis" and right = "scientist" - or - wrong = "scince" and right = "science" - or - wrong = "scinece" and right = "science" - or - wrong = "scirpt" and right = "script" - or - wrong = "scoll" and right = "scroll" - or - wrong = "screenwrighter" and right = "screenwriter" - or - wrong = "scrutinity" and right = "scrutiny" - or - wrong = "scuptures" and right = "sculptures" - or - wrong = "seach" and right = "search" - or - wrong = "seached" and right = "searched" - or - wrong = "seaches" and right = "searches" - or - wrong = "secceeded" and right = "seceded" - or - wrong = "secceeded" and right = "succeeded" - or - wrong = "seceed" and right = "secede" - or - wrong = "seceed" and right = "succeed" - or - wrong = "seceeded" and right = "seceded" - or - wrong = "seceeded" and right = "succeeded" - or - wrong = "secratary" and right = "secretary" - or - wrong = "secretery" and right = "secretary" - or - wrong = "sedereal" and right = "sidereal" - or - wrong = "seeked" and right = "sought" - or - wrong = "segementation" and right = "segmentation" - or - wrong = "seguoys" and right = "segues" - or - wrong = "seige" and right = "siege" - or - wrong = "seing" and right = "seeing" - or - wrong = "seinor" and right = "senior" - or - wrong = "seldomly" and right = "seldom" - or - wrong = "senarios" and right = "scenarios" - or - wrong = "sence" and right = "sense" - or - wrong = "sence" and right = "since" - or - wrong = "senstive" and right = "sensitive" - or - wrong = "sensure" and right = "censure" - or - wrong = "seperate" and right = "separate" - or - wrong = "seperated" and right = "separated" - or - wrong = "seperately" and right = "separately" - or - wrong = "seperates" and right = "separates" - or - wrong = "seperating" and right = "separating" - or - wrong = "seperation" and right = "separation" - or - wrong = "seperatism" and right = "separatism" - or - wrong = "seperatist" and right = "separatist" - or - wrong = "seperator" and right = "separator" - or - wrong = "sepina" and right = "subpoena" - or - wrong = "sepulchure" and right = "sepulcher" - or - wrong = "sepulchure" and right = "sepulchre" - or - wrong = "sepulcre" and right = "sepulcher" - or - wrong = "sepulcre" and right = "sepulchre" - or - wrong = "sergent" and right = "sergeant" - or - wrong = "settelement" and right = "settlement" - or - wrong = "settlment" and right = "settlement" - or - wrong = "settting" and right = "setting" - or - wrong = "severeal" and right = "several" - or - wrong = "severley" and right = "severely" - or - wrong = "severly" and right = "severely" - or - wrong = "sevice" and right = "service" - or - wrong = "shadasloo" and right = "shadaloo" - or - wrong = "shaddow" and right = "shadow" - or - wrong = "shadoloo" and right = "shadaloo" - or - wrong = "shamen" and right = "shaman" - or - wrong = "shamen" and right = "shamans" - or - wrong = "sheat" and right = "cheat" - or - wrong = "sheat" and right = "sheath" - or - wrong = "sheat" and right = "sheet" - or - wrong = "sheild" and right = "shield" - or - wrong = "sherif" and right = "sheriff" - or - wrong = "shineing" and right = "shining" - or - wrong = "shiped" and right = "shipped" - or - wrong = "shiping" and right = "shipping" - or - wrong = "shopkeeepers" and right = "shopkeepers" - or - wrong = "shorly" and right = "shortly" - or - wrong = "shoudl" and right = "should" - or - wrong = "shoudln" and right = "should" - or - wrong = "shreak" and right = "shriek" - or - wrong = "shrinked" and right = "shrunk" - or - wrong = "sicne" and right = "since" - or - wrong = "sideral" and right = "sidereal" - or - wrong = "sieze" and right = "seize" - or - wrong = "sieze" and right = "size" - or - wrong = "siezed" and right = "seized" - or - wrong = "siezed" and right = "sized" - or - wrong = "siezing" and right = "seizing" - or - wrong = "siezing" and right = "sizing" - or - wrong = "siezure" and right = "seizure" - or - wrong = "siezures" and right = "seizures" - or - wrong = "siginificant" and right = "significant" - or - wrong = "signficant" and right = "significant" - or - wrong = "signficiant" and right = "significant" - or - wrong = "signfies" and right = "signifies" - or - wrong = "signifantly" and right = "significantly" - or - wrong = "significently" and right = "significantly" - or - wrong = "signifigant" and right = "significant" - or - wrong = "signifigantly" and right = "significantly" - or - wrong = "signitories" and right = "signatories" - or - wrong = "signitory" and right = "signatory" - or - wrong = "similarily" and right = "similarly" - or - wrong = "similiar" and right = "similar" - or - wrong = "similiarity" and right = "similarity" - or - wrong = "similiarly" and right = "similarly" - or - wrong = "simmilar" and right = "similar" - or - wrong = "simpley" and right = "simply" - or - wrong = "simplier" and right = "simpler" - or - wrong = "simultanous" and right = "simultaneous" - or - wrong = "simultanously" and right = "simultaneously" - or - wrong = "sincerley" and right = "sincerely" - or - wrong = "singsog" and right = "singsong" - or - wrong = "sinse" and right = "since" - or - wrong = "sinse" and right = "sines" - or - wrong = "sionist" and right = "zionist" - or - wrong = "sionists" and right = "zionists" - or - wrong = "sixtin" and right = "sistine" - or - wrong = "skagerak" and right = "skagerrak" - or - wrong = "skateing" and right = "skating" - or - wrong = "slaugterhouses" and right = "slaughterhouses" - or - wrong = "slighly" and right = "slightly" - or - wrong = "slippy" and right = "slippery" - or - wrong = "slowy" and right = "slowly" - or - wrong = "smae" and right = "same" - or - wrong = "smealting" and right = "smelting" - or - wrong = "smoe" and right = "some" - or - wrong = "sneeks" and right = "sneaks" - or - wrong = "snese" and right = "sneeze" - or - wrong = "socalism" and right = "socialism" - or - wrong = "socities" and right = "societies" - or - wrong = "soem" and right = "some" - or - wrong = "sofware" and right = "software" - or - wrong = "sohw" and right = "show" - or - wrong = "soilders" and right = "soldiers" - or - wrong = "solatary" and right = "solitary" - or - wrong = "soley" and right = "solely" - or - wrong = "soliders" and right = "soldiers" - or - wrong = "soliliquy" and right = "soliloquy" - or - wrong = "soluable" and right = "soluble" - or - wrong = "somene" and right = "someone" - or - wrong = "somtimes" and right = "sometimes" - or - wrong = "somwhere" and right = "somewhere" - or - wrong = "sophicated" and right = "sophisticated" - or - wrong = "sophmore" and right = "sophomore" - or - wrong = "sorceror" and right = "sorcerer" - or - wrong = "sorrounding" and right = "surrounding" - or - wrong = "sotry" and right = "story" - or - wrong = "sotyr" and right = "satyr" - or - wrong = "sotyr" and right = "story" - or - wrong = "soudn" and right = "sound" - or - wrong = "soudns" and right = "sounds" - or - wrong = "sould" and right = "could" - or - wrong = "sould" and right = "should" - or - wrong = "sould" and right = "sold" - or - wrong = "sould" and right = "soul" - or - wrong = "sountrack" and right = "soundtrack" - or - wrong = "sourth" and right = "south" - or - wrong = "sourthern" and right = "southern" - or - wrong = "souvenier" and right = "souvenir" - or - wrong = "souveniers" and right = "souvenirs" - or - wrong = "soveits" and right = "soviets" - or - wrong = "sovereignity" and right = "sovereignty" - or - wrong = "soverign" and right = "sovereign" - or - wrong = "soverignity" and right = "sovereignty" - or - wrong = "soverignty" and right = "sovereignty" - or - wrong = "spainish" and right = "spanish" - or - wrong = "speach" and right = "speech" - or - wrong = "specfic" and right = "specific" - or - wrong = "speciallized" and right = "specialised" - or - wrong = "speciallized" and right = "specialized" - or - wrong = "specif" and right = "specific" - or - wrong = "specif" and right = "specify" - or - wrong = "specifiying" and right = "specifying" - or - wrong = "speciman" and right = "specimen" - or - wrong = "spectauclar" and right = "spectacular" - or - wrong = "spectaulars" and right = "spectaculars" - or - wrong = "spects" and right = "aspects" - or - wrong = "spects" and right = "expects" - or - wrong = "spectum" and right = "spectrum" - or - wrong = "speices" and right = "species" - or - wrong = "spendour" and right = "splendour" - or - wrong = "spermatozoan" and right = "spermatozoon" - or - wrong = "spoace" and right = "space" - or - wrong = "sponser" and right = "sponsor" - or - wrong = "sponsered" and right = "sponsored" - or - wrong = "spontanous" and right = "spontaneous" - or - wrong = "sponzored" and right = "sponsored" - or - wrong = "spoonfulls" and right = "spoonfuls" - or - wrong = "sppeches" and right = "speeches" - or - wrong = "spreaded" and right = "spread" - or - wrong = "sprech" and right = "speech" - or - wrong = "spred" and right = "spread" - or - wrong = "spriritual" and right = "spiritual" - or - wrong = "spritual" and right = "spiritual" - or - wrong = "sqaure" and right = "square" - or - wrong = "sring" and right = "string" - or - wrong = "stablility" and right = "stability" - or - wrong = "stainlees" and right = "stainless" - or - wrong = "staion" and right = "station" - or - wrong = "standars" and right = "standards" - or - wrong = "stange" and right = "strange" - or - wrong = "startegic" and right = "strategic" - or - wrong = "startegies" and right = "strategies" - or - wrong = "startegy" and right = "strategy" - or - wrong = "stateman" and right = "statesman" - or - wrong = "statememts" and right = "statements" - or - wrong = "statment" and right = "statement" - or - wrong = "steriods" and right = "steroids" - or - wrong = "sterotypes" and right = "stereotypes" - or - wrong = "stilus" and right = "stylus" - or - wrong = "stingent" and right = "stringent" - or - wrong = "stiring" and right = "stirring" - or - wrong = "stirrs" and right = "stirs" - or - wrong = "stlye" and right = "style" - or - wrong = "stomache" and right = "stomach" - or - wrong = "stong" and right = "strong" - or - wrong = "stopry" and right = "story" - or - wrong = "storeis" and right = "stories" - or - wrong = "storise" and right = "stories" - or - wrong = "stornegst" and right = "strongest" - or - wrong = "stoyr" and right = "story" - or - wrong = "stpo" and right = "stop" - or - wrong = "stradegies" and right = "strategies" - or - wrong = "stradegy" and right = "strategy" - or - wrong = "strat" and right = "start" - or - wrong = "strat" and right = "strata" - or - wrong = "stratagically" and right = "strategically" - or - wrong = "streemlining" and right = "streamlining" - or - wrong = "stregth" and right = "strength" - or - wrong = "strenghen" and right = "strengthen" - or - wrong = "strenghened" and right = "strengthened" - or - wrong = "strenghening" and right = "strengthening" - or - wrong = "strenght" and right = "strength" - or - wrong = "strenghten" and right = "strengthen" - or - wrong = "strenghtened" and right = "strengthened" - or - wrong = "strenghtening" and right = "strengthening" - or - wrong = "strengtened" and right = "strengthened" - or - wrong = "strenous" and right = "strenuous" - or - wrong = "strictist" and right = "strictest" - or - wrong = "strikely" and right = "strikingly" - or - wrong = "strnad" and right = "strand" - or - wrong = "stroy" and right = "destroy" - or - wrong = "stroy" and right = "story" - or - wrong = "structual" and right = "structural" - or - wrong = "stubborness" and right = "stubbornness" - or - wrong = "stucture" and right = "structure" - or - wrong = "stuctured" and right = "structured" - or - wrong = "studdy" and right = "study" - or - wrong = "studing" and right = "studying" - or - wrong = "stuggling" and right = "struggling" - or - wrong = "sturcture" and right = "structure" - or - wrong = "subcatagories" and right = "subcategories" - or - wrong = "subcatagory" and right = "subcategory" - or - wrong = "subconsiously" and right = "subconsciously" - or - wrong = "subjudgation" and right = "subjugation" - or - wrong = "submachne" and right = "submachine" - or - wrong = "subpecies" and right = "subspecies" - or - wrong = "subsidary" and right = "subsidiary" - or - wrong = "subsiduary" and right = "subsidiary" - or - wrong = "subsquent" and right = "subsequent" - or - wrong = "subsquently" and right = "subsequently" - or - wrong = "substace" and right = "substance" - or - wrong = "substancial" and right = "substantial" - or - wrong = "substatial" and right = "substantial" - or - wrong = "substituded" and right = "substituted" - or - wrong = "substract" and right = "subtract" - or - wrong = "substracted" and right = "subtracted" - or - wrong = "substracting" and right = "subtracting" - or - wrong = "substraction" and right = "subtraction" - or - wrong = "substracts" and right = "subtracts" - or - wrong = "subtances" and right = "substances" - or - wrong = "subterranian" and right = "subterranean" - or - wrong = "suburburban" and right = "suburban" - or - wrong = "succceeded" and right = "succeeded" - or - wrong = "succcesses" and right = "successes" - or - wrong = "succedded" and right = "succeeded" - or - wrong = "succeded" and right = "succeeded" - or - wrong = "succeds" and right = "succeeds" - or - wrong = "succesful" and right = "successful" - or - wrong = "succesfully" and right = "successfully" - or - wrong = "succesfuly" and right = "successfully" - or - wrong = "succesion" and right = "succession" - or - wrong = "succesive" and right = "successive" - or - wrong = "successfull" and right = "successful" - or - wrong = "successully" and right = "successfully" - or - wrong = "succsess" and right = "success" - or - wrong = "succsessfull" and right = "successful" - or - wrong = "suceed" and right = "succeed" - or - wrong = "suceeded" and right = "succeeded" - or - wrong = "suceeding" and right = "succeeding" - or - wrong = "suceeds" and right = "succeeds" - or - wrong = "sucesful" and right = "successful" - or - wrong = "sucesfully" and right = "successfully" - or - wrong = "sucesfuly" and right = "successfully" - or - wrong = "sucesion" and right = "succession" - or - wrong = "sucess" and right = "success" - or - wrong = "sucesses" and right = "successes" - or - wrong = "sucessful" and right = "successful" - or - wrong = "sucessfull" and right = "successful" - or - wrong = "sucessfully" and right = "successfully" - or - wrong = "sucessfuly" and right = "successfully" - or - wrong = "sucession" and right = "succession" - or - wrong = "sucessive" and right = "successive" - or - wrong = "sucessor" and right = "successor" - or - wrong = "sucessot" and right = "successor" - or - wrong = "sucide" and right = "suicide" - or - wrong = "sucidial" and right = "suicidal" - or - wrong = "sudent" and right = "student" - or - wrong = "sudents" and right = "students" - or - wrong = "sufferage" and right = "suffrage" - or - wrong = "sufferred" and right = "suffered" - or - wrong = "sufferring" and right = "suffering" - or - wrong = "sufficent" and right = "sufficient" - or - wrong = "sufficently" and right = "sufficiently" - or - wrong = "sumary" and right = "summary" - or - wrong = "sunglases" and right = "sunglasses" - or - wrong = "suop" and right = "soup" - or - wrong = "superceeded" and right = "superseded" - or - wrong = "superintendant" and right = "superintendent" - or - wrong = "suphisticated" and right = "sophisticated" - or - wrong = "suplimented" and right = "supplemented" - or - wrong = "suported" and right = "supported" - or - wrong = "supose" and right = "suppose" - or - wrong = "suposed" and right = "supposed" - or - wrong = "suposedly" and right = "supposedly" - or - wrong = "suposes" and right = "supposes" - or - wrong = "suposing" and right = "supposing" - or - wrong = "supplamented" and right = "supplemented" - or - wrong = "suppliementing" and right = "supplementing" - or - wrong = "suppoed" and right = "supposed" - or - wrong = "supposingly" and right = "supposedly" - or - wrong = "suppy" and right = "supply" - or - wrong = "suprassing" and right = "surpassing" - or - wrong = "supress" and right = "suppress" - or - wrong = "supressed" and right = "suppressed" - or - wrong = "supresses" and right = "suppresses" - or - wrong = "supressing" and right = "suppressing" - or - wrong = "suprise" and right = "surprise" - or - wrong = "suprised" and right = "surprised" - or - wrong = "suprising" and right = "surprising" - or - wrong = "suprisingly" and right = "surprisingly" - or - wrong = "suprize" and right = "surprise" - or - wrong = "suprized" and right = "surprised" - or - wrong = "suprizing" and right = "surprising" - or - wrong = "suprizingly" and right = "surprisingly" - or - wrong = "surfce" and right = "surface" - or - wrong = "surley" and right = "surely" - or - wrong = "surley" and right = "surly" - or - wrong = "suround" and right = "surround" - or - wrong = "surounded" and right = "surrounded" - or - wrong = "surounding" and right = "surrounding" - or - wrong = "suroundings" and right = "surroundings" - or - wrong = "surounds" and right = "surrounds" - or - wrong = "surplanted" and right = "supplanted" - or - wrong = "surpress" and right = "suppress" - or - wrong = "surpressed" and right = "suppressed" - or - wrong = "surprize" and right = "surprise" - or - wrong = "surprized" and right = "surprised" - or - wrong = "surprizing" and right = "surprising" - or - wrong = "surprizingly" and right = "surprisingly" - or - wrong = "surrended" and right = "surrendered" - or - wrong = "surrended" and right = "surrounded" - or - wrong = "surrepetitious" and right = "surreptitious" - or - wrong = "surrepetitiously" and right = "surreptitiously" - or - wrong = "surreptious" and right = "surreptitious" - or - wrong = "surreptiously" and right = "surreptitiously" - or - wrong = "surronded" and right = "surrounded" - or - wrong = "surrouded" and right = "surrounded" - or - wrong = "surrouding" and right = "surrounding" - or - wrong = "surrundering" and right = "surrendering" - or - wrong = "surveilence" and right = "surveillance" - or - wrong = "surveill" and right = "surveil" - or - wrong = "surveyer" and right = "surveyor" - or - wrong = "surviver" and right = "survivor" - or - wrong = "survivers" and right = "survivors" - or - wrong = "survivied" and right = "survived" - or - wrong = "suseptable" and right = "susceptible" - or - wrong = "suseptible" and right = "susceptible" - or - wrong = "suspention" and right = "suspension" - or - wrong = "swaer" and right = "swear" - or - wrong = "swaers" and right = "swears" - or - wrong = "swepth" and right = "swept" - or - wrong = "swiming" and right = "swimming" - or - wrong = "syas" and right = "says" - or - wrong = "symetrical" and right = "symmetrical" - or - wrong = "symetrically" and right = "symmetrically" - or - wrong = "symetry" and right = "symmetry" - or - wrong = "symettric" and right = "symmetric" - or - wrong = "symmetral" and right = "symmetric" - or - wrong = "symmetricaly" and right = "symmetrically" - or - wrong = "synagouge" and right = "synagogue" - or - wrong = "syncronization" and right = "synchronization" - or - wrong = "synonomous" and right = "synonymous" - or - wrong = "synonymns" and right = "synonyms" - or - wrong = "synphony" and right = "symphony" - or - wrong = "syphyllis" and right = "syphilis" - or - wrong = "sypmtoms" and right = "symptoms" - or - wrong = "syrap" and right = "syrup" - or - wrong = "sysmatically" and right = "systematically" - or - wrong = "sytem" and right = "system" - or - wrong = "sytle" and right = "style" - or - wrong = "tabacco" and right = "tobacco" - or - wrong = "tahn" and right = "than" - or - wrong = "taht" and right = "that" - or - wrong = "talekd" and right = "talked" - or - wrong = "targetted" and right = "targeted" - or - wrong = "targetting" and right = "targeting" - or - wrong = "tast" and right = "taste" - or - wrong = "tath" and right = "that" - or - wrong = "tatoo" and right = "tattoo" - or - wrong = "tattooes" and right = "tattoos" - or - wrong = "taxanomic" and right = "taxonomic" - or - wrong = "taxanomy" and right = "taxonomy" - or - wrong = "teached" and right = "taught" - or - wrong = "techician" and right = "technician" - or - wrong = "techicians" and right = "technicians" - or - wrong = "techiniques" and right = "techniques" - or - wrong = "technitian" and right = "technician" - or - wrong = "technnology" and right = "technology" - or - wrong = "technolgy" and right = "technology" - or - wrong = "teh" and right = "the" - or - wrong = "tehy" and right = "they" - or - wrong = "telelevision" and right = "television" - or - wrong = "televsion" and right = "television" - or - wrong = "telphony" and right = "telephony" - or - wrong = "temerature" and right = "temperature" - or - wrong = "tempalte" and right = "template" - or - wrong = "tempaltes" and right = "templates" - or - wrong = "temparate" and right = "temperate" - or - wrong = "temperarily" and right = "temporarily" - or - wrong = "temperment" and right = "temperament" - or - wrong = "tempertaure" and right = "temperature" - or - wrong = "temperture" and right = "temperature" - or - wrong = "temprary" and right = "temporary" - or - wrong = "tenacle" and right = "tentacle" - or - wrong = "tenacles" and right = "tentacles" - or - wrong = "tendacy" and right = "tendency" - or - wrong = "tendancies" and right = "tendencies" - or - wrong = "tendancy" and right = "tendency" - or - wrong = "tepmorarily" and right = "temporarily" - or - wrong = "terrestial" and right = "terrestrial" - or - wrong = "terriories" and right = "territories" - or - wrong = "terriory" and right = "territory" - or - wrong = "territorist" and right = "terrorist" - or - wrong = "territoy" and right = "territory" - or - wrong = "terroist" and right = "terrorist" - or - wrong = "testiclular" and right = "testicular" - or - wrong = "testomony" and right = "testimony" - or - wrong = "tghe" and right = "the" - or - wrong = "thast" and right = "that" - or - wrong = "theather" and right = "theater" - or - wrong = "theese" and right = "these" - or - wrong = "theif" and right = "thief" - or - wrong = "theives" and right = "thieves" - or - wrong = "themselfs" and right = "themselves" - or - wrong = "themslves" and right = "themselves" - or - wrong = "ther" and right = "the" - or - wrong = "ther" and right = "their" - or - wrong = "ther" and right = "there" - or - wrong = "therafter" and right = "thereafter" - or - wrong = "therby" and right = "thereby" - or - wrong = "theri" and right = "their" - or - wrong = "thgat" and right = "that" - or - wrong = "thge" and right = "the" - or - wrong = "thier" and right = "their" - or - wrong = "thign" and right = "thing" - or - wrong = "thigns" and right = "things" - or - wrong = "thigsn" and right = "things" - or - wrong = "thikn" and right = "think" - or - wrong = "thikning" and right = "thickening" - or - wrong = "thikning" and right = "thinking" - or - wrong = "thikns" and right = "thinks" - or - wrong = "thiunk" and right = "think" - or - wrong = "thn" and right = "then" - or - wrong = "thna" and right = "than" - or - wrong = "thne" and right = "then" - or - wrong = "thnig" and right = "thing" - or - wrong = "thnigs" and right = "things" - or - wrong = "thoughout" and right = "throughout" - or - wrong = "threatend" and right = "threatened" - or - wrong = "threatning" and right = "threatening" - or - wrong = "threee" and right = "three" - or - wrong = "threshhold" and right = "threshold" - or - wrong = "thrid" and right = "third" - or - wrong = "throrough" and right = "thorough" - or - wrong = "throughly" and right = "thoroughly" - or - wrong = "throught" and right = "thought" - or - wrong = "throught" and right = "through" - or - wrong = "throught" and right = "throughout" - or - wrong = "througout" and right = "throughout" - or - wrong = "thru" and right = "through" - or - wrong = "thsi" and right = "this" - or - wrong = "thsoe" and right = "those" - or - wrong = "thta" and right = "that" - or - wrong = "thyat" and right = "that" - or - wrong = "tiem" and right = "tim" - or - wrong = "tiem" and right = "time" - or - wrong = "tihkn" and right = "think" - or - wrong = "tihs" and right = "this" - or - wrong = "timne" and right = "time" - or - wrong = "tiome" and right = "time" - or - wrong = "tiome" and right = "tome" - or - wrong = "tje" and right = "the" - or - wrong = "tjhe" and right = "the" - or - wrong = "tjpanishad" and right = "upanishad" - or - wrong = "tkae" and right = "take" - or - wrong = "tkaes" and right = "takes" - or - wrong = "tkaing" and right = "taking" - or - wrong = "tlaking" and right = "talking" - or - wrong = "tobbaco" and right = "tobacco" - or - wrong = "todya" and right = "today" - or - wrong = "toghether" and right = "together" - or - wrong = "toke" and right = "took" - or - wrong = "tolerence" and right = "tolerance" - or - wrong = "tolkein" and right = "tolkien" - or - wrong = "tomatos" and right = "tomatoes" - or - wrong = "tommorow" and right = "tomorrow" - or - wrong = "tommorrow" and right = "tomorrow" - or - wrong = "tongiht" and right = "tonight" - or - wrong = "toriodal" and right = "toroidal" - or - wrong = "tormenters" and right = "tormentors" - or - wrong = "tornadoe" and right = "tornado" - or - wrong = "torpeados" and right = "torpedoes" - or - wrong = "torpedos" and right = "torpedoes" - or - wrong = "tortise" and right = "tortoise" - or - wrong = "toubles" and right = "troubles" - or - wrong = "tounge" and right = "tongue" - or - wrong = "tourch" and right = "torch" - or - wrong = "tourch" and right = "touch" - or - wrong = "towords" and right = "towards" - or - wrong = "towrad" and right = "toward" - or - wrong = "tradionally" and right = "traditionally" - or - wrong = "traditionaly" and right = "traditionally" - or - wrong = "traditionnal" and right = "traditional" - or - wrong = "traditition" and right = "tradition" - or - wrong = "tradtionally" and right = "traditionally" - or - wrong = "trafficed" and right = "trafficked" - or - wrong = "trafficing" and right = "trafficking" - or - wrong = "trafic" and right = "traffic" - or - wrong = "trancendent" and right = "transcendent" - or - wrong = "trancending" and right = "transcending" - or - wrong = "tranform" and right = "transform" - or - wrong = "tranformed" and right = "transformed" - or - wrong = "transcendance" and right = "transcendence" - or - wrong = "transcendant" and right = "transcendent" - or - wrong = "transcendentational" and right = "transcendental" - or - wrong = "transcripting" and right = "transcribing" - or - wrong = "transcripting" and right = "transcription" - or - wrong = "transending" and right = "transcending" - or - wrong = "transesxuals" and right = "transsexuals" - or - wrong = "transfered" and right = "transferred" - or - wrong = "transfering" and right = "transferring" - or - wrong = "transformaton" and right = "transformation" - or - wrong = "transistion" and right = "transition" - or - wrong = "translater" and right = "translator" - or - wrong = "translaters" and right = "translators" - or - wrong = "transmissable" and right = "transmissible" - or - wrong = "transporation" and right = "transportation" - or - wrong = "tremelo" and right = "tremolo" - or - wrong = "tremelos" and right = "tremolos" - or - wrong = "treshold" and right = "threshold" - or - wrong = "triguered" and right = "triggered" - or - wrong = "triology" and right = "trilogy" - or - wrong = "troling" and right = "trolling" - or - wrong = "troup" and right = "troupe" - or - wrong = "troups" and right = "troops" - or - wrong = "troups" and right = "troupes" - or - wrong = "truely" and right = "truly" - or - wrong = "trustworthyness" and right = "trustworthiness" - or - wrong = "turnk" and right = "trunk" - or - wrong = "turnk" and right = "turnkey" - or - wrong = "tuscon" and right = "tucson" - or - wrong = "tust" and right = "trust" - or - wrong = "twelth" and right = "twelfth" - or - wrong = "twon" and right = "town" - or - wrong = "twpo" and right = "two" - or - wrong = "tyhat" and right = "that" - or - wrong = "tyhe" and right = "they" - or - wrong = "typcial" and right = "typical" - or - wrong = "typicaly" and right = "typically" - or - wrong = "tyranies" and right = "tyrannies" - or - wrong = "tyrany" and right = "tyranny" - or - wrong = "tyrranies" and right = "tyrannies" - or - wrong = "tyrrany" and right = "tyranny" - or - wrong = "ubiquitious" and right = "ubiquitous" - or - wrong = "ublisher" and right = "publisher" - or - wrong = "udpate" and right = "update" - or - wrong = "uise" and right = "use" - or - wrong = "ukranian" and right = "ukrainian" - or - wrong = "ultimely" and right = "ultimately" - or - wrong = "unacompanied" and right = "unaccompanied" - or - wrong = "unahppy" and right = "unhappy" - or - wrong = "unanymous" and right = "unanimous" - or - wrong = "unathorised" and right = "unauthorised" - or - wrong = "unavailible" and right = "unavailable" - or - wrong = "unballance" and right = "unbalance" - or - wrong = "unbeknowst" and right = "unbeknownst" - or - wrong = "unbeleivable" and right = "unbelievable" - or - wrong = "uncertainity" and right = "uncertainty" - or - wrong = "unchallengable" and right = "unchallengeable" - or - wrong = "unchangable" and right = "unchangeable" - or - wrong = "uncompetive" and right = "uncompetitive" - or - wrong = "unconcious" and right = "unconscious" - or - wrong = "unconciousness" and right = "unconsciousness" - or - wrong = "unconfortability" and right = "discomfort" - or - wrong = "uncontitutional" and right = "unconstitutional" - or - wrong = "unconvential" and right = "unconventional" - or - wrong = "undecideable" and right = "undecidable" - or - wrong = "understoon" and right = "understood" - or - wrong = "undesireable" and right = "undesirable" - or - wrong = "undetecable" and right = "undetectable" - or - wrong = "undoubtely" and right = "undoubtedly" - or - wrong = "undreground" and right = "underground" - or - wrong = "uneccesary" and right = "unnecessary" - or - wrong = "unecessary" and right = "unnecessary" - or - wrong = "unequalities" and right = "inequalities" - or - wrong = "unforetunately" and right = "unfortunately" - or - wrong = "unforgetable" and right = "unforgettable" - or - wrong = "unforgiveable" and right = "unforgivable" - or - wrong = "unforseen" and right = "unforeseen" - or - wrong = "unfortunatley" and right = "unfortunately" - or - wrong = "unfortunatly" and right = "unfortunately" - or - wrong = "unfourtunately" and right = "unfortunately" - or - wrong = "unihabited" and right = "uninhabited" - or - wrong = "unilateraly" and right = "unilaterally" - or - wrong = "unilatreal" and right = "unilateral" - or - wrong = "unilatreally" and right = "unilaterally" - or - wrong = "uninterruped" and right = "uninterrupted" - or - wrong = "uninterupted" and right = "uninterrupted" - or - wrong = "unintialized" and right = "uninitialized" - or - wrong = "unitesstates" and right = "unitedstates" - or - wrong = "univeral" and right = "universal" - or - wrong = "univeristies" and right = "universities" - or - wrong = "univeristy" and right = "university" - or - wrong = "univerity" and right = "university" - or - wrong = "universtiy" and right = "university" - or - wrong = "univesities" and right = "universities" - or - wrong = "univesity" and right = "university" - or - wrong = "unkown" and right = "unknown" - or - wrong = "unlikey" and right = "unlikely" - or - wrong = "unmanouverable" and right = "unmaneuverable" - or - wrong = "unmanouverable" and right = "unmanoeuvrable" - or - wrong = "unmistakeably" and right = "unmistakably" - or - wrong = "unneccesarily" and right = "unnecessarily" - or - wrong = "unneccesary" and right = "unnecessary" - or - wrong = "unneccessarily" and right = "unnecessarily" - or - wrong = "unneccessary" and right = "unnecessary" - or - wrong = "unnecesarily" and right = "unnecessarily" - or - wrong = "unnecesary" and right = "unnecessary" - or - wrong = "unoffical" and right = "unofficial" - or - wrong = "unoperational" and right = "nonoperational" - or - wrong = "unoticeable" and right = "unnoticeable" - or - wrong = "unplease" and right = "displease" - or - wrong = "unplesant" and right = "unpleasant" - or - wrong = "unprecendented" and right = "unprecedented" - or - wrong = "unprecidented" and right = "unprecedented" - or - wrong = "unrepentent" and right = "unrepentant" - or - wrong = "unrepetant" and right = "unrepentant" - or - wrong = "unrepetent" and right = "unrepentant" - or - wrong = "unsed" and right = "unsaid" - or - wrong = "unsed" and right = "unused" - or - wrong = "unsed" and right = "used" - or - wrong = "unsubstanciated" and right = "unsubstantiated" - or - wrong = "unsuccesful" and right = "unsuccessful" - or - wrong = "unsuccesfully" and right = "unsuccessfully" - or - wrong = "unsuccessfull" and right = "unsuccessful" - or - wrong = "unsucesful" and right = "unsuccessful" - or - wrong = "unsucesfuly" and right = "unsuccessfully" - or - wrong = "unsucessful" and right = "unsuccessful" - or - wrong = "unsucessfull" and right = "unsuccessful" - or - wrong = "unsucessfully" and right = "unsuccessfully" - or - wrong = "unsuprised" and right = "unsurprised" - or - wrong = "unsuprising" and right = "unsurprising" - or - wrong = "unsuprisingly" and right = "unsurprisingly" - or - wrong = "unsuprized" and right = "unsurprised" - or - wrong = "unsuprizing" and right = "unsurprising" - or - wrong = "unsuprizingly" and right = "unsurprisingly" - or - wrong = "unsurprized" and right = "unsurprised" - or - wrong = "unsurprizing" and right = "unsurprising" - or - wrong = "unsurprizingly" and right = "unsurprisingly" - or - wrong = "untill" and right = "until" - or - wrong = "untranslateable" and right = "untranslatable" - or - wrong = "unuseable" and right = "unusable" - or - wrong = "unusuable" and right = "unusable" - or - wrong = "unviersity" and right = "university" - or - wrong = "unwarrented" and right = "unwarranted" - or - wrong = "unweildly" and right = "unwieldy" - or - wrong = "unwieldly" and right = "unwieldy" - or - wrong = "upcomming" and right = "upcoming" - or - wrong = "upgradded" and right = "upgraded" - or - wrong = "usally" and right = "usually" - or - wrong = "useage" and right = "usage" - or - wrong = "usefull" and right = "useful" - or - wrong = "usefuly" and right = "usefully" - or - wrong = "useing" and right = "using" - or - wrong = "usualy" and right = "usually" - or - wrong = "ususally" and right = "usually" - or - wrong = "vaccum" and right = "vacuum" - or - wrong = "vaccume" and right = "vacuum" - or - wrong = "vacinity" and right = "vicinity" - or - wrong = "vaguaries" and right = "vagaries" - or - wrong = "vaieties" and right = "varieties" - or - wrong = "vailidty" and right = "validity" - or - wrong = "valetta" and right = "valletta" - or - wrong = "valuble" and right = "valuable" - or - wrong = "valueable" and right = "valuable" - or - wrong = "varations" and right = "variations" - or - wrong = "varient" and right = "variant" - or - wrong = "variey" and right = "variety" - or - wrong = "varing" and right = "varying" - or - wrong = "varities" and right = "varieties" - or - wrong = "varity" and right = "variety" - or - wrong = "vasall" and right = "vassal" - or - wrong = "vasalls" and right = "vassals" - or - wrong = "vaule" and right = "value" - or - wrong = "vegatarian" and right = "vegetarian" - or - wrong = "vegitable" and right = "vegetable" - or - wrong = "vegitables" and right = "vegetables" - or - wrong = "vegtable" and right = "vegetable" - or - wrong = "vehicule" and right = "vehicle" - or - wrong = "vell" and right = "well" - or - wrong = "venemous" and right = "venomous" - or - wrong = "vengance" and right = "vengeance" - or - wrong = "vengence" and right = "vengeance" - or - wrong = "verfication" and right = "verification" - or - wrong = "verison" and right = "version" - or - wrong = "verisons" and right = "versions" - or - wrong = "vermillion" and right = "vermilion" - or - wrong = "versitilaty" and right = "versatility" - or - wrong = "versitlity" and right = "versatility" - or - wrong = "vetween" and right = "between" - or - wrong = "veyr" and right = "very" - or - wrong = "vigeur" and right = "vigor" - or - wrong = "vigeur" and right = "vigour" - or - wrong = "vigeur" and right = "vigueur" - or - wrong = "vigilence" and right = "vigilance" - or - wrong = "vigourous" and right = "vigorous" - or - wrong = "villian" and right = "villain" - or - wrong = "villification" and right = "vilification" - or - wrong = "villify" and right = "vilify" - or - wrong = "villin" and right = "villain" - or - wrong = "villin" and right = "villein" - or - wrong = "villin" and right = "villi" - or - wrong = "vincinity" and right = "vicinity" - or - wrong = "violentce" and right = "violence" - or - wrong = "virtualy" and right = "virtually" - or - wrong = "virutal" and right = "virtual" - or - wrong = "virutally" and right = "virtually" - or - wrong = "visable" and right = "visible" - or - wrong = "visably" and right = "visibly" - or - wrong = "visting" and right = "visiting" - or - wrong = "vistors" and right = "visitors" - or - wrong = "vitories" and right = "victories" - or - wrong = "volcanoe" and right = "volcano" - or - wrong = "voleyball" and right = "volleyball" - or - wrong = "volontary" and right = "voluntary" - or - wrong = "volonteer" and right = "volunteer" - or - wrong = "volonteered" and right = "volunteered" - or - wrong = "volonteering" and right = "volunteering" - or - wrong = "volonteers" and right = "volunteers" - or - wrong = "volounteer" and right = "volunteer" - or - wrong = "volounteered" and right = "volunteered" - or - wrong = "volounteering" and right = "volunteering" - or - wrong = "volounteers" and right = "volunteers" - or - wrong = "volumne" and right = "volume" - or - wrong = "vreity" and right = "variety" - or - wrong = "vrey" and right = "very" - or - wrong = "vriety" and right = "variety" - or - wrong = "vulnerablility" and right = "vulnerability" - or - wrong = "vyer" and right = "very" - or - wrong = "vyre" and right = "very" - or - wrong = "waht" and right = "what" - or - wrong = "warantee" and right = "warranty" - or - wrong = "wardobe" and right = "wardrobe" - or - wrong = "warrent" and right = "warrant" - or - wrong = "warrriors" and right = "warriors" - or - wrong = "wass" and right = "was" - or - wrong = "watn" and right = "want" - or - wrong = "wayword" and right = "wayward" - or - wrong = "weaponary" and right = "weaponry" - or - wrong = "weas" and right = "was" - or - wrong = "wehn" and right = "when" - or - wrong = "weild" and right = "wield" - or - wrong = "weild" and right = "wild" - or - wrong = "weilded" and right = "wielded" - or - wrong = "wendsay" and right = "wednesday" - or - wrong = "wensday" and right = "wednesday" - or - wrong = "wereabouts" and right = "whereabouts" - or - wrong = "whant" and right = "want" - or - wrong = "whants" and right = "wants" - or - wrong = "whcih" and right = "which" - or - wrong = "wheras" and right = "whereas" - or - wrong = "wherease" and right = "whereas" - or - wrong = "whereever" and right = "wherever" - or - wrong = "whic" and right = "which" - or - wrong = "whihc" and right = "which" - or - wrong = "whith" and right = "with" - or - wrong = "whlch" and right = "which" - or - wrong = "whn" and right = "when" - or - wrong = "wholey" and right = "wholly" - or - wrong = "wholy" and right = "holy" - or - wrong = "wholy" and right = "wholly" - or - wrong = "whta" and right = "what" - or - wrong = "whther" and right = "whether" - or - wrong = "wich" and right = "which" - or - wrong = "wich" and right = "witch" - or - wrong = "widesread" and right = "widespread" - or - wrong = "wief" and right = "wife" - or - wrong = "wierd" and right = "weird" - or - wrong = "wiew" and right = "view" - or - wrong = "wih" and right = "with" - or - wrong = "wiht" and right = "with" - or - wrong = "wille" and right = "will" - or - wrong = "willingless" and right = "willingness" - or - wrong = "willk" and right = "will" - or - wrong = "wirting" and right = "writing" - or - wrong = "withdrawl" and right = "withdraw" - or - wrong = "withdrawl" and right = "withdrawal" - or - wrong = "witheld" and right = "withheld" - or - wrong = "withh" and right = "with" - or - wrong = "withing" and right = "within" - or - wrong = "withold" and right = "withhold" - or - wrong = "witht" and right = "with" - or - wrong = "witn" and right = "with" - or - wrong = "wiull" and right = "will" - or - wrong = "wnat" and right = "want" - or - wrong = "wnated" and right = "wanted" - or - wrong = "wnats" and right = "wants" - or - wrong = "wohle" and right = "whole" - or - wrong = "wokr" and right = "work" - or - wrong = "wokring" and right = "working" - or - wrong = "wonderfull" and right = "wonderful" - or - wrong = "wordlwide" and right = "worldwide" - or - wrong = "workststion" and right = "workstation" - or - wrong = "worls" and right = "world" - or - wrong = "worstened" and right = "worsened" - or - wrong = "woudl" and right = "would" - or - wrong = "wresters" and right = "wrestlers" - or - wrong = "wriet" and right = "write" - or - wrong = "writen" and right = "written" - or - wrong = "wroet" and right = "wrote" - or - wrong = "wrok" and right = "work" - or - wrong = "wroking" and right = "working" - or - wrong = "wtih" and right = "with" - or - wrong = "wupport" and right = "support" - or - wrong = "xenophoby" and right = "xenophobia" - or - wrong = "yaching" and right = "yachting" - or - wrong = "yaer" and right = "year" - or - wrong = "yaerly" and right = "yearly" - or - wrong = "yaers" and right = "years" - or - wrong = "yatch" and right = "yacht" - or - wrong = "yearm" and right = "year" - or - wrong = "yeasr" and right = "years" - or - wrong = "yeild" and right = "yield" - or - wrong = "yeilding" and right = "yielding" - or - wrong = "yementite" and right = "yemeni" - or - wrong = "yementite" and right = "yemenite" - or - wrong = "yera" and right = "year" - or - wrong = "yeras" and right = "years" - or - wrong = "yersa" and right = "years" - or - wrong = "yotube" and right = "youtube" - or - wrong = "youseff" and right = "yousef" - or - wrong = "youself" and right = "yourself" - or - wrong = "yrea" and right = "year" - or - wrong = "ytou" and right = "you" - or - wrong = "yuo" and right = "you" - or - wrong = "zeebra" and right = "zebra" -} +import codeql.typos.TypoDatabase as DB + +/** DEPRECATED: Use the `codeql/typos` pack instead. */ +deprecated predicate typos = DB::typos/2; diff --git a/ql/ql/src/qlpack.yml b/ql/ql/src/qlpack.yml index 9ae2cb13787..26450f8d718 100644 --- a/ql/ql/src/qlpack.yml +++ b/ql/ql/src/qlpack.yml @@ -5,3 +5,5 @@ dbscheme: ql.dbscheme suites: codeql-suites defaultSuiteFile: codeql-suites/ql-code-scanning.qls extractor: ql +dependencies: + codeql/typos: 0.0.1-dev diff --git a/ql/ql/src/queries/bugs/MissingSanitizerGuardCase.ql b/ql/ql/src/queries/bugs/MissingSanitizerGuardCase.ql new file mode 100644 index 00000000000..325d2877cb3 --- /dev/null +++ b/ql/ql/src/queries/bugs/MissingSanitizerGuardCase.ql @@ -0,0 +1,28 @@ +/** + * @name Unmentioned guard class + * @description A sanitizer guard should be included in the `isSanitizerGuard` predicate. + * @kind problem + * @problem.severity warning + * @id ql/unmentioned-guard + * @tags correctness + * maintainability + * @precision medium + */ + +import ql + +class SanGuard extends Class { + SanGuard() { + exists(Class sup | + sup = this.getASuperType().getResolvedType().(ClassType).getDeclaration() and + sup.getName() = ["SanitizerGuardNode", "SanitizerGuard", "BarrierGuardNode", "BarrierGuard"] and + sup.getLocation().getFile() != this.getLocation().getFile() + ) + } +} + +from SanGuard guard +where + not exists(TypeExpr t | t.getResolvedType().(ClassType).getDeclaration() = guard) and + not guard.hasAnnotation("deprecated") +select guard, "Guard class is not mentioned anywhere" diff --git a/ruby/Cargo.lock b/ruby/Cargo.lock index 2e2f205ce02..ed1b9e2bd3b 100644 Binary files a/ruby/Cargo.lock and b/ruby/Cargo.lock differ diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/old.dbscheme b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/old.dbscheme new file mode 100644 index 00000000000..3595c826de6 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/old.dbscheme @@ -0,0 +1,1435 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_call_operator = @ruby_reserved_word + +@ruby_underscore_expression = @ruby_assignment | @ruby_binary | @ruby_break | @ruby_call | @ruby_next | @ruby_operator_assignment | @ruby_return | @ruby_unary | @ruby_underscore_arg | @ruby_yield + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable + +@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable + +@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant + +@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic + +@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern + +@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_heredoc_beginning | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric + +@ruby_underscore_pattern_top_expr_body = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_underscore_pattern_expr + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_heredoc_beginning | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_underscore_simple_numeric | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_simple_numeric = @ruby_complex | @ruby_rational | @ruby_token_float | @ruby_token_integer + +@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier + +@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref +); + +#keyset[ruby_alternative_pattern, index] +ruby_alternative_pattern_alternatives( + int ruby_alternative_pattern: @ruby_alternative_pattern ref, + int index: int ref, + unique int alternatives: @ruby_underscore_pattern_expr_basic ref +); + +ruby_alternative_pattern_def( + unique int id: @ruby_alternative_pattern +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array +); + +ruby_array_pattern_class( + unique int ruby_array_pattern: @ruby_array_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_array_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_array_pattern, index] +ruby_array_pattern_child( + int ruby_array_pattern: @ruby_array_pattern ref, + int index: int ref, + unique int child: @ruby_array_pattern_child_type ref +); + +ruby_array_pattern_def( + unique int id: @ruby_array_pattern +); + +ruby_as_pattern_def( + unique int id: @ruby_as_pattern, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_pattern_expr ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_rescue_modifier | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_expression + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block +); + +@ruby_binary_left_type = @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_underscore_expression ref +); + +ruby_block_body( + unique int ruby_block: @ruby_block ref, + unique int body: @ruby_block_body ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +ruby_block_def( + unique int id: @ruby_block +); + +ruby_block_argument_child( + unique int ruby_block_argument: @ruby_block_argument ref, + unique int child: @ruby_underscore_arg ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument +); + +@ruby_block_body_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block_body, index] +ruby_block_body_child( + int ruby_block_body: @ruby_block_body ref, + int index: int ref, + unique int child: @ruby_block_body_child_type ref +); + +ruby_block_body_def( + unique int id: @ruby_block_body +); + +ruby_block_parameter_name( + unique int ruby_block_parameter: @ruby_block_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter +); + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_locals( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int locals: @ruby_token_identifier ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters +); + +@ruby_body_statement_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_body_statement, index] +ruby_body_statement_child( + int ruby_body_statement: @ruby_body_statement ref, + int index: int ref, + unique int child: @ruby_body_statement_child_type ref +); + +ruby_body_statement_def( + unique int id: @ruby_body_statement +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_token_operator | @ruby_underscore_variable + +ruby_call_method( + unique int ruby_call: @ruby_call ref, + unique int method: @ruby_call_method_type ref +); + +ruby_call_operator( + unique int ruby_call: @ruby_call ref, + unique int operator: @ruby_underscore_call_operator ref +); + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_underscore_primary ref +); + +ruby_call_def( + unique int id: @ruby_call +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__ +); + +#keyset[ruby_case_match, index] +ruby_case_match_clauses( + int ruby_case_match: @ruby_case_match ref, + int index: int ref, + unique int clauses: @ruby_in_clause ref +); + +ruby_case_match_else( + unique int ruby_case_match: @ruby_case_match ref, + unique int else: @ruby_else ref +); + +ruby_case_match_def( + unique int id: @ruby_case_match, + int value: @ruby_underscore_statement ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string +); + +ruby_class_body( + unique int ruby_class: @ruby_class ref, + unique int body: @ruby_body_statement ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref +); + +@ruby_complex_child_type = @ruby_rational | @ruby_token_float | @ruby_token_integer + +ruby_complex_def( + unique int id: @ruby_complex, + int child: @ruby_complex_child_type ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do +); + +ruby_do_block_body( + unique int ruby_do_block: @ruby_do_block ref, + unique int body: @ruby_body_statement ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions +); + +ruby_expression_reference_pattern_def( + unique int id: @ruby_expression_reference_pattern, + int value: @ruby_underscore_expression ref +); + +ruby_find_pattern_class( + unique int ruby_find_pattern: @ruby_find_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_find_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_find_pattern, index] +ruby_find_pattern_child( + int ruby_find_pattern: @ruby_find_pattern ref, + int index: int ref, + unique int child: @ruby_find_pattern_child_type ref +); + +ruby_find_pattern_def( + unique int id: @ruby_find_pattern +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash +); + +ruby_hash_pattern_class( + unique int ruby_hash_pattern: @ruby_hash_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_hash_pattern_child_type = @ruby_hash_splat_parameter | @ruby_keyword_pattern | @ruby_token_hash_splat_nil + +#keyset[ruby_hash_pattern, index] +ruby_hash_pattern_child( + int ruby_hash_pattern: @ruby_hash_pattern ref, + int index: int ref, + unique int child: @ruby_hash_pattern_child_type ref +); + +ruby_hash_pattern_def( + unique int id: @ruby_hash_pattern +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref +); + +ruby_if_guard_def( + unique int id: @ruby_if_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref +); + +ruby_in_clause_body( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int body: @ruby_then ref +); + +@ruby_in_clause_guard_type = @ruby_if_guard | @ruby_unless_guard + +ruby_in_clause_guard( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int guard: @ruby_in_clause_guard_type ref +); + +ruby_in_clause_def( + unique int id: @ruby_in_clause, + int pattern: @ruby_underscore_pattern_top_expr_body ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_nonlocal_variable | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref +); + +@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol + +ruby_keyword_pattern_value( + unique int ruby_keyword_pattern: @ruby_keyword_pattern ref, + unique int value: @ruby_underscore_pattern_expr ref +); + +ruby_keyword_pattern_def( + unique int id: @ruby_keyword_pattern, + int key__: @ruby_keyword_pattern_key_type ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list +); + +@ruby_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_method_body( + unique int ruby_method: @ruby_method ref, + unique int body: @ruby_method_body_type ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters +); + +ruby_module_body( + unique int ruby_module: @ruby_module ref, + unique int body: @ruby_body_statement ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_rescue_modifier | @ruby_underscore_expression + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_value( + unique int ruby_pair: @ruby_pair ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref +); + +ruby_parenthesized_pattern_def( + unique int id: @ruby_parenthesized_pattern, + int child: @ruby_underscore_pattern_expr ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program +); + +@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_range_begin_type ref +); + +@ruby_range_end_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_range_end_type ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue +); + +@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_rescue_modifier_body_type ref, + int handler: @ruby_underscore_expression ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list +); + +@ruby_scope_resolution_scope_type = @ruby_underscore_pattern_constant | @ruby_underscore_primary + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_scope_resolution_scope_type ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_token_constant ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref +); + +ruby_singleton_class_body( + unique int ruby_singleton_class: @ruby_singleton_class ref, + unique int body: @ruby_body_statement ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref +); + +@ruby_singleton_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_singleton_method_body( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int body: @ruby_singleton_method_body_type ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__ +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell +); + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_underscore_expression ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then +); + +@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref +); + +ruby_unless_guard_def( + unique int id: @ruby_unless_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable + +ruby_variable_reference_pattern_def( + unique int id: @ruby_variable_reference_pattern, + int name: @ruby_variable_reference_pattern_name_type ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_constant +| 5 = @ruby_token_empty_statement +| 6 = @ruby_token_encoding +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_file +| 10 = @ruby_token_float +| 11 = @ruby_token_forward_argument +| 12 = @ruby_token_forward_parameter +| 13 = @ruby_token_global_variable +| 14 = @ruby_token_hash_key_symbol +| 15 = @ruby_token_hash_splat_nil +| 16 = @ruby_token_heredoc_beginning +| 17 = @ruby_token_heredoc_content +| 18 = @ruby_token_heredoc_end +| 19 = @ruby_token_identifier +| 20 = @ruby_token_instance_variable +| 21 = @ruby_token_integer +| 22 = @ruby_token_line +| 23 = @ruby_token_nil +| 24 = @ruby_token_operator +| 25 = @ruby_token_self +| 26 = @ruby_token_simple_symbol +| 27 = @ruby_token_string_content +| 28 = @ruby_token_super +| 29 = @ruby_token_true +| 30 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_body | @ruby_block_parameter | @ruby_block_parameters | @ruby_body_statement | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_info( + unique int node: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + +erb_comment_directive_child( + unique int erb_comment_directive: @erb_comment_directive ref, + unique int child: @erb_token_comment ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive +); + +erb_directive_child( + unique int erb_directive: @erb_directive ref, + unique int child: @erb_token_code ref +); + +erb_directive_def( + unique int id: @erb_directive +); + +erb_graphql_directive_child( + unique int erb_graphql_directive: @erb_graphql_directive ref, + unique int child: @erb_token_code ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive +); + +erb_output_directive_child( + unique int erb_output_directive: @erb_output_directive ref, + unique int child: @erb_token_code ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_info( + unique int node: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby.dbscheme b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby.dbscheme new file mode 100644 index 00000000000..4ba51641799 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby.dbscheme @@ -0,0 +1,1433 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_call_operator = @ruby_reserved_word + +@ruby_underscore_expression = @ruby_assignment | @ruby_binary | @ruby_break | @ruby_call | @ruby_next | @ruby_operator_assignment | @ruby_return | @ruby_unary | @ruby_underscore_arg | @ruby_yield + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable + +@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable + +@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant + +@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic + +@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern + +@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_heredoc_beginning | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric + +@ruby_underscore_pattern_top_expr_body = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_underscore_pattern_expr + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_heredoc_beginning | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_underscore_simple_numeric | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_simple_numeric = @ruby_complex | @ruby_rational | @ruby_token_float | @ruby_token_integer + +@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier + +@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref +); + +#keyset[ruby_alternative_pattern, index] +ruby_alternative_pattern_alternatives( + int ruby_alternative_pattern: @ruby_alternative_pattern ref, + int index: int ref, + unique int alternatives: @ruby_underscore_pattern_expr_basic ref +); + +ruby_alternative_pattern_def( + unique int id: @ruby_alternative_pattern +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array +); + +ruby_array_pattern_class( + unique int ruby_array_pattern: @ruby_array_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_array_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_array_pattern, index] +ruby_array_pattern_child( + int ruby_array_pattern: @ruby_array_pattern ref, + int index: int ref, + unique int child: @ruby_array_pattern_child_type ref +); + +ruby_array_pattern_def( + unique int id: @ruby_array_pattern +); + +ruby_as_pattern_def( + unique int id: @ruby_as_pattern, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_pattern_expr ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_rescue_modifier | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_expression + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block +); + +@ruby_binary_left_type = @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_underscore_expression ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block +); + +ruby_block_argument_child( + unique int ruby_block_argument: @ruby_block_argument ref, + unique int child: @ruby_underscore_arg ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument +); + +ruby_block_parameter_name( + unique int ruby_block_parameter: @ruby_block_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter +); + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_locals( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int locals: @ruby_token_identifier ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_token_operator | @ruby_underscore_variable + +ruby_call_method( + unique int ruby_call: @ruby_call ref, + unique int method: @ruby_call_method_type ref +); + +ruby_call_operator( + unique int ruby_call: @ruby_call ref, + unique int operator: @ruby_underscore_call_operator ref +); + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_underscore_primary ref +); + +ruby_call_def( + unique int id: @ruby_call +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__ +); + +#keyset[ruby_case_match, index] +ruby_case_match_clauses( + int ruby_case_match: @ruby_case_match ref, + int index: int ref, + unique int clauses: @ruby_in_clause ref +); + +ruby_case_match_else( + unique int ruby_case_match: @ruby_case_match ref, + unique int else: @ruby_else ref +); + +ruby_case_match_def( + unique int id: @ruby_case_match, + int value: @ruby_underscore_statement ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref +); + +@ruby_complex_child_type = @ruby_rational | @ruby_token_float | @ruby_token_integer + +ruby_complex_def( + unique int id: @ruby_complex, + int child: @ruby_complex_child_type ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions +); + +ruby_expression_reference_pattern_def( + unique int id: @ruby_expression_reference_pattern, + int value: @ruby_underscore_expression ref +); + +ruby_find_pattern_class( + unique int ruby_find_pattern: @ruby_find_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_find_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_find_pattern, index] +ruby_find_pattern_child( + int ruby_find_pattern: @ruby_find_pattern ref, + int index: int ref, + unique int child: @ruby_find_pattern_child_type ref +); + +ruby_find_pattern_def( + unique int id: @ruby_find_pattern +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash +); + +ruby_hash_pattern_class( + unique int ruby_hash_pattern: @ruby_hash_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_hash_pattern_child_type = @ruby_hash_splat_parameter | @ruby_keyword_pattern | @ruby_token_hash_splat_nil + +#keyset[ruby_hash_pattern, index] +ruby_hash_pattern_child( + int ruby_hash_pattern: @ruby_hash_pattern ref, + int index: int ref, + unique int child: @ruby_hash_pattern_child_type ref +); + +ruby_hash_pattern_def( + unique int id: @ruby_hash_pattern +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref +); + +ruby_if_guard_def( + unique int id: @ruby_if_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref +); + +ruby_in_clause_body( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int body: @ruby_then ref +); + +@ruby_in_clause_guard_type = @ruby_if_guard | @ruby_unless_guard + +ruby_in_clause_guard( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int guard: @ruby_in_clause_guard_type ref +); + +ruby_in_clause_def( + unique int id: @ruby_in_clause, + int pattern: @ruby_underscore_pattern_top_expr_body ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref +); + +@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol + +ruby_keyword_pattern_value( + unique int ruby_keyword_pattern: @ruby_keyword_pattern ref, + unique int value: @ruby_underscore_pattern_expr ref +); + +ruby_keyword_pattern_def( + unique int id: @ruby_keyword_pattern, + int key__: @ruby_keyword_pattern_key_type ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_rescue_modifier | @ruby_underscore_expression + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_value( + unique int ruby_pair: @ruby_pair ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref +); + +ruby_parenthesized_pattern_def( + unique int id: @ruby_parenthesized_pattern, + int child: @ruby_underscore_pattern_expr ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program +); + +@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_range_begin_type ref +); + +@ruby_range_end_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_range_end_type ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue +); + +@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_rescue_modifier_body_type ref, + int handler: @ruby_underscore_expression ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list +); + +@ruby_scope_resolution_scope_type = @ruby_underscore_pattern_constant | @ruby_underscore_primary + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_scope_resolution_scope_type ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_token_constant ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__ +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell +); + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_underscore_expression ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then +); + +@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref +); + +ruby_unless_guard_def( + unique int id: @ruby_unless_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable + +ruby_variable_reference_pattern_def( + unique int id: @ruby_variable_reference_pattern, + int name: @ruby_variable_reference_pattern_name_type ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_constant +| 5 = @ruby_token_empty_statement +| 6 = @ruby_token_encoding +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_file +| 10 = @ruby_token_float +| 11 = @ruby_token_forward_argument +| 12 = @ruby_token_forward_parameter +| 13 = @ruby_token_global_variable +| 14 = @ruby_token_hash_key_symbol +| 15 = @ruby_token_hash_splat_nil +| 16 = @ruby_token_heredoc_beginning +| 17 = @ruby_token_heredoc_content +| 18 = @ruby_token_heredoc_end +| 19 = @ruby_token_identifier +| 20 = @ruby_token_instance_variable +| 21 = @ruby_token_integer +| 22 = @ruby_token_line +| 23 = @ruby_token_nil +| 24 = @ruby_token_operator +| 25 = @ruby_token_self +| 26 = @ruby_token_simple_symbol +| 27 = @ruby_token_string_content +| 28 = @ruby_token_super +| 29 = @ruby_token_true +| 30 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_info( + unique int node: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + +erb_comment_directive_child( + unique int erb_comment_directive: @erb_comment_directive ref, + unique int child: @erb_token_comment ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive +); + +erb_directive_child( + unique int erb_directive: @erb_directive ref, + unique int child: @erb_token_code ref +); + +erb_directive_def( + unique int id: @erb_directive +); + +erb_graphql_directive_child( + unique int erb_graphql_directive: @erb_graphql_directive ref, + unique int child: @erb_token_code ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive +); + +erb_output_directive_child( + unique int erb_output_directive: @erb_output_directive ref, + unique int child: @erb_token_code ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_info( + unique int node: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_ast_node_info.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_ast_node_info.ql new file mode 100644 index 00000000000..c418da98507 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_ast_node_info.ql @@ -0,0 +1,28 @@ +class AstNode extends @ruby_ast_node_parent { + string toString() { none() } +} + +class Location extends @location { + string toString() { none() } +} + +class Wrapper = @ruby_body_statement or @ruby_block_body; + +predicate astNodeInfo(AstNode child, AstNode parent, int primaryIndex, int secondaryIndex) { + not parent instanceof Wrapper and + exists(AstNode node, Location l | + ruby_ast_node_info(node, parent, primaryIndex, _) and + ( + not node instanceof Wrapper and secondaryIndex = 0 and child = node + or + node instanceof Wrapper and ruby_ast_node_info(child, node, secondaryIndex, _) + ) + ) +} + +from AstNode node, AstNode parent, int parent_index, Location loc +where + node = + rank[parent_index + 1](AstNode n, int i, int j | astNodeInfo(n, parent, i, j) | n order by i, j) and + ruby_ast_node_info(node, _, _, loc) +select node, parent, parent_index, loc diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_block_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_block_child.ql new file mode 100644 index 00000000000..f12816e6f44 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_block_child.ql @@ -0,0 +1,7 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_block, AstNode body, int index, AstNode child +where ruby_block_body(ruby_block, body) and ruby_block_body_child(body, index, child) +select ruby_block, index, child diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_class_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_class_child.ql new file mode 100644 index 00000000000..bba0cc7ec3a --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_class_child.ql @@ -0,0 +1,7 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_class, AstNode body, int index, AstNode child +where ruby_class_body(ruby_class, body) and ruby_body_statement_child(body, index, child) +select ruby_class, index, child diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_do_block_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_do_block_child.ql new file mode 100644 index 00000000000..d08bb83a41c --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_do_block_child.ql @@ -0,0 +1,8 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_do_block, AstNode body, int index, AstNode child +where ruby_do_block_body(ruby_do_block, body) and ruby_body_statement_child(body, index, child) +select ruby_do_block, index, child + diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_method_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_method_child.ql new file mode 100644 index 00000000000..fffa33ea4c7 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_method_child.ql @@ -0,0 +1,14 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_method, int index, AstNode child +where + exists(AstNode body | + ruby_method_body(ruby_method, body) and ruby_body_statement_child(body, index, child) + ) + or + ruby_method_body(ruby_method, child) and + not child instanceof @ruby_body_statement and + index = 0 +select ruby_method, index, child diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_module_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_module_child.ql new file mode 100644 index 00000000000..c7ea0dcdadf --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_module_child.ql @@ -0,0 +1,8 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_module, AstNode body, int index, AstNode child +where ruby_module_body(ruby_module, body) and ruby_body_statement_child(body, index, child) +select ruby_module, index, child + diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_class_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_class_child.ql new file mode 100644 index 00000000000..3d2e45d0ec6 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_class_child.ql @@ -0,0 +1,8 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_singleton_class, AstNode body, int index, AstNode child +where ruby_singleton_class_body(ruby_singleton_class, body) and ruby_body_statement_child(body, index, child) +select ruby_singleton_class, index, child + diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_method_child.ql b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_method_child.ql new file mode 100644 index 00000000000..cc76fecefeb --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/ruby_singleton_method_child.ql @@ -0,0 +1,15 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_singleton_method, int index, AstNode child +where + exists(AstNode body | + ruby_singleton_method_body(ruby_singleton_method, body) and + ruby_body_statement_child(body, index, child) + ) + or + ruby_singleton_method_body(ruby_singleton_method, child) and + not child instanceof @ruby_body_statement and + index = 0 +select ruby_singleton_method, index, child diff --git a/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/upgrade.properties b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/upgrade.properties new file mode 100644 index 00000000000..6d90aac9b38 --- /dev/null +++ b/ruby/downgrades/3595c826de6db850f16b9da265a54dbf24dd3126/upgrade.properties @@ -0,0 +1,30 @@ +description: Wrap class, module, method, and block bodies in a named node +compatibility: full + +ruby_block_body.rel: delete +ruby_block_body_def.rel: delete +ruby_block_body_child.rel: delete +ruby_block_child.rel: run ruby_block_child.qlo + +ruby_body_statement_child.rel: delete +ruby_body_statement_def.rel: delete + +ruby_class_body.rel: delete +ruby_class_child.rel: run ruby_class_child.qlo + +ruby_do_block_body.rel: delete +ruby_do_block_child.rel: run ruby_do_block_child.qlo + +ruby_method_body.rel: delete +ruby_method_child.rel: run ruby_method_child.qlo + +ruby_module_body.rel: delete +ruby_module_child.rel: run ruby_module_child.qlo + +ruby_singleton_class_body.rel: delete +ruby_singleton_class_child.rel: run ruby_singleton_class_child.qlo + +ruby_singleton_method_body.rel: delete +ruby_singleton_method_child.rel: run ruby_singleton_method_child.qlo + +ruby_ast_node_info.rel: run ruby_ast_node_info.qlo \ No newline at end of file diff --git a/ruby/extractor/Cargo.toml b/ruby/extractor/Cargo.toml index c1a7c4424b9..a5c753b6398 100644 --- a/ruby/extractor/Cargo.toml +++ b/ruby/extractor/Cargo.toml @@ -11,7 +11,7 @@ flate2 = "1.0" node-types = { path = "../node-types" } tree-sitter = "0.19" tree-sitter-embedded-template = { git = "https://github.com/tree-sitter/tree-sitter-embedded-template.git", rev = "1a538da253d73f896b9f6c0c7d79cda58791ac5c" } -tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "e75d04404c9dd71ad68850d5c672b226d5e694f3" } +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "ad1043283b1f9daf4aad381b6a81f18a5a27fe7e" } clap = "3.0" tracing = "0.1" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } diff --git a/ruby/generator/Cargo.toml b/ruby/generator/Cargo.toml index 5a70c43d7b3..493557e03d4 100644 --- a/ruby/generator/Cargo.toml +++ b/ruby/generator/Cargo.toml @@ -12,4 +12,4 @@ node-types = { path = "../node-types" } tracing = "0.1" tracing-subscriber = { version = "0.3.3", features = ["env-filter"] } tree-sitter-embedded-template = { git = "https://github.com/tree-sitter/tree-sitter-embedded-template.git", rev = "1a538da253d73f896b9f6c0c7d79cda58791ac5c" } -tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "e75d04404c9dd71ad68850d5c672b226d5e694f3" } +tree-sitter-ruby = { git = "https://github.com/tree-sitter/tree-sitter-ruby.git", rev = "ad1043283b1f9daf4aad381b6a81f18a5a27fe7e" } diff --git a/ruby/ql/lib/change-notes/2022-09-12-uppercase.md b/ruby/ql/lib/change-notes/2022-09-12-uppercase.md new file mode 100644 index 00000000000..996861f1c2c --- /dev/null +++ b/ruby/ql/lib/change-notes/2022-09-12-uppercase.md @@ -0,0 +1,5 @@ +--- +category: deprecated +--- +* Some classes/modules with upper-case acronyms in their name have been renamed to follow our style-guide. + The old name still exists as a deprecated alias. \ No newline at end of file diff --git a/ruby/ql/lib/codeql/ruby/AST.qll b/ruby/ql/lib/codeql/ruby/AST.qll index 49edd048301..a9f8f8b5f9f 100644 --- a/ruby/ql/lib/codeql/ruby/AST.qll +++ b/ruby/ql/lib/codeql/ruby/AST.qll @@ -60,10 +60,10 @@ class AstNode extends TAstNode { final string getPrimaryQlClasses() { result = concat(this.getAPrimaryQlClass(), ",") } /** Gets the enclosing module, if any. */ - ModuleBase getEnclosingModule() { result = getEnclosingModule(scopeOfInclSynth(this)) } + final ModuleBase getEnclosingModule() { result = getEnclosingModule(scopeOfInclSynth(this)) } /** Gets the enclosing method, if any. */ - MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) } + final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) } /** Gets a textual representation of this node. */ cached diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 4272b24145d..8cec5f0d190 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -33,7 +33,7 @@ module API { * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`. * * For example, a simplified way to get arguments to `Foo.bar` would be - * ```codeql + * ```ql * API::getTopLevelMember("Foo").getMethod("bar").getParameter(0).asSink() * ``` * diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll index 582b3bb7fdb..8189ca0b81a 100644 --- a/ruby/ql/lib/codeql/ruby/Concepts.qll +++ b/ruby/ql/lib/codeql/ruby/Concepts.qll @@ -222,7 +222,7 @@ class HtmlEscaping extends Escaping { } /** Provides classes for modeling HTTP-related APIs. */ -module HTTP { +module Http { /** Provides classes for modeling HTTP servers. */ module Server { /** @@ -489,7 +489,7 @@ module HTTP { * Extend this class to model new APIs. If you want to refine existing API models, * extend `HttpResponse` instead. */ - abstract class Range extends HTTP::Server::HttpResponse::Range { + abstract class Range extends Http::Server::HttpResponse::Range { /** Gets the data-flow node that specifies the location of this HTTP redirect response. */ abstract DataFlow::Node getRedirectLocation(); } @@ -574,6 +574,9 @@ module HTTP { } } +/** DEPRECATED: Alias for Http */ +deprecated module HTTP = Http; + /** * A data flow node that executes an operating system command, * for instance by spawning a new process. @@ -635,16 +638,12 @@ module CodeExecution { * Extend this class to refine existing API models. If you want to model new APIs, * extend `XmlParserCall::Range` instead. */ -class XmlParserCall extends DataFlow::Node { - XmlParserCall::Range range; - - XmlParserCall() { this = range } - +class XmlParserCall extends DataFlow::Node instanceof XmlParserCall::Range { /** Gets the argument that specifies the XML content to be parsed. */ - DataFlow::Node getInput() { result = range.getInput() } + DataFlow::Node getInput() { result = super.getInput() } /** Holds if this XML parser call is configured to process external entities */ - predicate externalEntitiesEnabled() { range.externalEntitiesEnabled() } + predicate externalEntitiesEnabled() { super.externalEntitiesEnabled() } } /** Provides a class for modeling new XML parsing APIs. */ @@ -814,13 +813,9 @@ module CookieSecurityConfigurationSetting { * Extend this class to refine existing API models. If you want to model new APIs, * extend `Logging::Range` instead. */ -class Logging extends DataFlow::Node { - Logging::Range range; - - Logging() { this = range } - +class Logging extends DataFlow::Node instanceof Logging::Range { /** Gets an input that is logged. */ - DataFlow::Node getAnInput() { result = range.getAnInput() } + DataFlow::Node getAnInput() { result = super.getAnInput() } } /** Provides a class for modeling new logging mechanisms. */ diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll index 167f5f90d1e..93cb8961f81 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Expr.qll @@ -51,25 +51,35 @@ class Ensure extends StmtSequence, TEnsure { // Not defined by dispatch, as it should not be exposed Ruby::AstNode getBodyStmtChild(TBodyStmt b, int i) { - result = any(Ruby::Method g | b = TMethod(g)).getChild(i) - or - result = any(Ruby::SingletonMethod g | b = TSingletonMethod(g)).getChild(i) - or - exists(Ruby::Lambda g | b = TLambda(g) | - result = g.getBody().(Ruby::DoBlock).getChild(i) or - result = g.getBody().(Ruby::Block).getChild(i) + exists(Ruby::Method g, Ruby::AstNode body | b = TMethod(g) and body = g.getBody() | + result = body.(Ruby::BodyStatement).getChild(i) + or + i = 0 and result = body and not body instanceof Ruby::BodyStatement ) or - result = any(Ruby::DoBlock g | b = TDoBlock(g)).getChild(i) + exists(Ruby::SingletonMethod g, Ruby::AstNode body | + b = TSingletonMethod(g) and body = g.getBody() + | + result = body.(Ruby::BodyStatement).getChild(i) + or + i = 0 and result = body and not body instanceof Ruby::BodyStatement + ) + or + exists(Ruby::Lambda g | b = TLambda(g) | + result = g.getBody().(Ruby::DoBlock).getBody().getChild(i) or + result = g.getBody().(Ruby::Block).getBody().getChild(i) + ) + or + result = any(Ruby::DoBlock g | b = TDoBlock(g)).getBody().getChild(i) or result = any(Ruby::Program g | b = TToplevel(g)).getChild(i) and not result instanceof Ruby::BeginBlock or - result = any(Ruby::Class g | b = TClassDeclaration(g)).getChild(i) + result = any(Ruby::Class g | b = TClassDeclaration(g)).getBody().getChild(i) or - result = any(Ruby::SingletonClass g | b = TSingletonClass(g)).getChild(i) + result = any(Ruby::SingletonClass g | b = TSingletonClass(g)).getBody().getChild(i) or - result = any(Ruby::Module g | b = TModuleDeclaration(g)).getChild(i) + result = any(Ruby::Module g | b = TModuleDeclaration(g)).getBody().getChild(i) or result = any(Ruby::Begin g | b = TBeginExpr(g)).getChild(i) } diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Method.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Method.qll index 0b923056441..075ac5fb8fa 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Method.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Method.qll @@ -15,7 +15,7 @@ class BraceBlockReal extends BraceBlock, TBraceBlockReal { toGenerated(result) = g.getParameters().getChild(n) } - final override Stmt getStmt(int i) { toGenerated(result) = g.getChild(i) } + final override Stmt getStmt(int i) { toGenerated(result) = g.getBody().getChild(i) } } /** diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll index e8e601e911b..8f5bde5aeec 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Scope.qll @@ -128,7 +128,7 @@ private AstNode specialParentOfInclSynth(AstNode n) { n = [ result.(Namespace).getScopeExpr(), result.(ClassDeclaration).getSuperclassExpr(), - result.(SingletonMethod).getObject() + result.(SingletonMethod).getObject(), result.(SingletonClass).getValue() ] } diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll index 7a3b6ef43ef..7d76c3d701a 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/TreeSitter.qll @@ -311,15 +311,15 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Block" } + /** Gets the node corresponding to the field `body`. */ + final BlockBody getBody() { ruby_block_body(this, result) } + /** Gets the node corresponding to the field `parameters`. */ final BlockParameters getParameters() { ruby_block_parameters(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_block_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - ruby_block_parameters(this, result) or ruby_block_child(this, _, result) + ruby_block_body(this, result) or ruby_block_parameters(this, result) } } @@ -335,6 +335,18 @@ module Ruby { final override AstNode getAFieldOrChild() { ruby_block_argument_child(this, result) } } + /** A class representing `block_body` nodes. */ + class BlockBody extends @ruby_block_body, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BlockBody" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { ruby_block_body_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { ruby_block_body_child(this, _, result) } + } + /** A class representing `block_parameter` nodes. */ class BlockParameter extends @ruby_block_parameter, AstNode { /** Gets the name of the primary QL class for this element. */ @@ -364,6 +376,18 @@ module Ruby { } } + /** A class representing `body_statement` nodes. */ + class BodyStatement extends @ruby_body_statement, AstNode { + /** Gets the name of the primary QL class for this element. */ + final override string getAPrimaryQlClass() { result = "BodyStatement" } + + /** Gets the `i`th child of this node. */ + final AstNode getChild(int i) { ruby_body_statement_child(this, i, result) } + + /** Gets a field or child node of this node. */ + final override AstNode getAFieldOrChild() { ruby_body_statement_child(this, _, result) } + } + /** A class representing `break` nodes. */ class Break extends @ruby_break, AstNode { /** Gets the name of the primary QL class for this element. */ @@ -468,20 +492,20 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Class" } + /** Gets the node corresponding to the field `body`. */ + final BodyStatement getBody() { ruby_class_body(this, result) } + /** Gets the node corresponding to the field `name`. */ final AstNode getName() { ruby_class_def(this, result) } /** Gets the node corresponding to the field `superclass`. */ final Superclass getSuperclass() { ruby_class_superclass(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_class_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { + ruby_class_body(this, result) or ruby_class_def(this, result) or - ruby_class_superclass(this, result) or - ruby_class_child(this, _, result) + ruby_class_superclass(this, result) } } @@ -592,15 +616,15 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "DoBlock" } + /** Gets the node corresponding to the field `body`. */ + final BodyStatement getBody() { ruby_do_block_body(this, result) } + /** Gets the node corresponding to the field `parameters`. */ final BlockParameters getParameters() { ruby_do_block_parameters(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_do_block_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - ruby_do_block_parameters(this, result) or ruby_do_block_child(this, _, result) + ruby_do_block_body(this, result) or ruby_do_block_parameters(this, result) } } @@ -1106,20 +1130,20 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Method" } + /** Gets the node corresponding to the field `body`. */ + final AstNode getBody() { ruby_method_body(this, result) } + /** Gets the node corresponding to the field `name`. */ final UnderscoreMethodName getName() { ruby_method_def(this, result) } /** Gets the node corresponding to the field `parameters`. */ final MethodParameters getParameters() { ruby_method_parameters(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_method_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { + ruby_method_body(this, result) or ruby_method_def(this, result) or - ruby_method_parameters(this, result) or - ruby_method_child(this, _, result) + ruby_method_parameters(this, result) } } @@ -1140,15 +1164,15 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "Module" } + /** Gets the node corresponding to the field `body`. */ + final BodyStatement getBody() { ruby_module_body(this, result) } + /** Gets the node corresponding to the field `name`. */ final AstNode getName() { ruby_module_def(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_module_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - ruby_module_def(this, result) or ruby_module_child(this, _, result) + ruby_module_body(this, result) or ruby_module_def(this, result) } } @@ -1504,15 +1528,15 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SingletonClass" } + /** Gets the node corresponding to the field `body`. */ + final BodyStatement getBody() { ruby_singleton_class_body(this, result) } + /** Gets the node corresponding to the field `value`. */ final UnderscoreArg getValue() { ruby_singleton_class_def(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_singleton_class_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { - ruby_singleton_class_def(this, result) or ruby_singleton_class_child(this, _, result) + ruby_singleton_class_body(this, result) or ruby_singleton_class_def(this, result) } } @@ -1521,6 +1545,9 @@ module Ruby { /** Gets the name of the primary QL class for this element. */ final override string getAPrimaryQlClass() { result = "SingletonMethod" } + /** Gets the node corresponding to the field `body`. */ + final AstNode getBody() { ruby_singleton_method_body(this, result) } + /** Gets the node corresponding to the field `name`. */ final UnderscoreMethodName getName() { ruby_singleton_method_def(this, result, _) } @@ -1530,15 +1557,12 @@ module Ruby { /** Gets the node corresponding to the field `parameters`. */ final MethodParameters getParameters() { ruby_singleton_method_parameters(this, result) } - /** Gets the `i`th child of this node. */ - final AstNode getChild(int i) { ruby_singleton_method_child(this, i, result) } - /** Gets a field or child node of this node. */ final override AstNode getAFieldOrChild() { + ruby_singleton_method_body(this, result) or ruby_singleton_method_def(this, result, _) or ruby_singleton_method_def(this, _, result) or - ruby_singleton_method_parameters(this, result) or - ruby_singleton_method_child(this, _, result) + ruby_singleton_method_parameters(this, result) } } diff --git a/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll index 64fcdbd669a..9e1e2c38de9 100644 --- a/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll +++ b/ruby/ql/lib/codeql/ruby/ast/internal/Variable.qll @@ -200,18 +200,18 @@ private module Cached { or i = any(Ruby::Binary x).getRight() or - i = any(Ruby::Block x).getChild(_) - or i = any(Ruby::BlockArgument x).getChild() or + i = any(Ruby::BlockBody x).getChild(_) + or + i = any(Ruby::BodyStatement x).getChild(_) + or i = any(Ruby::Call x).getReceiver() or i = any(Ruby::Case x).getValue() or i = any(Ruby::CaseMatch x).getValue() or - i = any(Ruby::Class x).getChild(_) - or i = any(Ruby::Conditional x).getCondition() or i = any(Ruby::Conditional x).getConsequence() @@ -220,8 +220,6 @@ private module Cached { or i = any(Ruby::Do x).getChild(_) or - i = any(Ruby::DoBlock x).getChild(_) - or i = any(Ruby::ElementReference x).getChild(_) or i = any(Ruby::ElementReference x).getObject() @@ -250,9 +248,7 @@ private module Cached { or i = any(Ruby::KeywordParameter x).getValue() or - i = any(Ruby::Method x).getChild(_) - or - i = any(Ruby::Module x).getChild(_) + i = any(Ruby::Method x).getBody() or i = any(Ruby::OperatorAssignment x).getRight() or @@ -282,9 +278,7 @@ private module Cached { or i = any(Ruby::SingletonClass x).getValue() or - i = any(Ruby::SingletonClass x).getChild(_) - or - i = any(Ruby::SingletonMethod x).getChild(_) + i = any(Ruby::SingletonMethod x).getBody() or i = any(Ruby::SingletonMethod x).getObject() or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll b/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll index 617bfd8678e..de85af6b938 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/RemoteFlowSources.qll @@ -13,13 +13,9 @@ private import codeql.ruby.Frameworks * Extend this class to refine existing API models. If you want to model new APIs, * extend `RemoteFlowSource::Range` instead. */ -class RemoteFlowSource extends DataFlow::Node { - RemoteFlowSource::Range self; - - RemoteFlowSource() { this = self } - +class RemoteFlowSource extends DataFlow::Node instanceof RemoteFlowSource::Range { /** Gets a string that describes the type of this remote flow source. */ - string getSourceType() { result = self.getSourceType() } + string getSourceType() { result = super.getSourceType() } } /** Provides a class for modeling new sources of remote user input. */ diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index acca30eb219..675009d0062 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -613,7 +613,7 @@ private module ParameterNodes { * where direct keyword matching is possible, since we construct a synthesized hash * splat argument (`SynthHashSplatArgumentNode`) at the call site, which means that * `taint(1)` will flow into `p1` both via normal keyword matching and via the synthesized - * nodes (and similarly for `p2`). However, this redunancy is OK since + * nodes (and similarly for `p2`). However, this redundancy is OK since * (a) it means that type-tracking through keyword arguments also works in most cases, * (b) read/store steps can be avoided when direct keyword matching is possible, and * hence access path limits are not a concern, and diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 36414d0b84b..2f7fc9453df 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -46,7 +46,7 @@ class ActionControllerControllerClass extends ClassDeclaration { * A public instance method defined within an `ActionController` controller class. * This may be the target of a route handler, if such a route is defined. */ -class ActionControllerActionMethod extends Method, HTTP::Server::RequestHandler::Range { +class ActionControllerActionMethod extends Method, Http::Server::RequestHandler::Range { private ActionControllerControllerClass controllerClass; ActionControllerActionMethod() { this = controllerClass.getAMethod() and not this.isPrivate() } @@ -128,7 +128,7 @@ abstract class ParamsCall extends MethodCall { * A `RemoteFlowSource::Range` to represent accessing the * ActionController parameters available via the `params` method. */ -class ParamsSource extends HTTP::Server::RequestInputAccess::Range { +class ParamsSource extends Http::Server::RequestInputAccess::Range { ParamsSource() { this.asExpr().getExpr() instanceof ParamsCall } override string getSourceType() { result = "ActionController::Metal#params" } @@ -145,7 +145,7 @@ abstract class CookiesCall extends MethodCall { * A `RemoteFlowSource::Range` to represent accessing the * ActionController parameters available via the `cookies` method. */ -class CookiesSource extends HTTP::Server::RequestInputAccess::Range { +class CookiesSource extends Http::Server::RequestInputAccess::Range { CookiesSource() { this.asExpr().getExpr() instanceof CookiesCall } override string getSourceType() { result = "ActionController::Metal#cookies" } @@ -213,7 +213,7 @@ class RedirectToCall extends ActionControllerContextCall { /** * A call to the `redirect_to` method, as an `HttpRedirectResponse`. */ -class ActionControllerRedirectResponse extends HTTP::Server::HttpRedirectResponse::Range { +class ActionControllerRedirectResponse extends Http::Server::HttpRedirectResponse::Range { RedirectToCall redirectToCall; ActionControllerRedirectResponse() { this.asExpr().getExpr() = redirectToCall } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll index fade252ce84..b1e3e07d2d0 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionView.qll @@ -127,7 +127,7 @@ abstract class RenderCall extends MethodCall { * A call to `render`, `render_to_body` or `render_to_string`, seen as an * `HttpResponse`. */ -private class RenderCallAsHttpResponse extends DataFlow::CallNode, HTTP::Server::HttpResponse::Range { +private class RenderCallAsHttpResponse extends DataFlow::CallNode, Http::Server::HttpResponse::Range { RenderCallAsHttpResponse() { this.asExpr().getExpr() instanceof RenderCall or this.asExpr().getExpr() instanceof RenderToCall diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll index 901552d637f..b212f420fc6 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll @@ -215,7 +215,7 @@ module ActiveResource { Collection getCollection() { result = this.getReceiver() } } - private class ModelClassMethodCallAsHttpRequest extends HTTP::Client::Request::Range, + private class ModelClassMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelClassMethodCall { ModelClass cls; @@ -239,7 +239,7 @@ module ActiveResource { override DataFlow::Node getResponseBody() { result = this } } - private class ModelInstanceMethodCallAsHttpRequest extends HTTP::Client::Request::Range, + private class ModelInstanceMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelInstanceMethodCall { ModelClass cls; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 2f98853ebbf..7e2a52ad357 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -167,7 +167,7 @@ private class GraphqlResolvableClass extends ClassDeclaration { * end * ``` */ -class GraphqlResolveMethod extends Method, HTTP::Server::RequestHandler::Range { +class GraphqlResolveMethod extends Method, Http::Server::RequestHandler::Range { private GraphqlResolvableClass resolvableClass; GraphqlResolveMethod() { this = resolvableClass.getMethod("resolve") } @@ -213,7 +213,7 @@ class GraphqlResolveMethod extends Method, HTTP::Server::RequestHandler::Range { * end * ``` */ -class GraphqlLoadMethod extends Method, HTTP::Server::RequestHandler::Range { +class GraphqlLoadMethod extends Method, Http::Server::RequestHandler::Range { private GraphqlResolvableClass resolvableClass; GraphqlLoadMethod() { @@ -347,7 +347,7 @@ private class GraphqlFieldArgumentDefinitionMethodCall extends GraphqlSchemaObje * end * ``` */ -class GraphqlFieldResolutionMethod extends Method, HTTP::Server::RequestHandler::Range { +class GraphqlFieldResolutionMethod extends Method, Http::Server::RequestHandler::Range { private GraphqlSchemaObjectClass schemaObjectClass; GraphqlFieldResolutionMethod() { diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll index c74958be716..0cca7e268dc 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Excon.qll @@ -24,7 +24,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * TODO: pipelining, streaming responses * https://github.com/excon/excon/blob/master/README.md */ -class ExconHttpRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class ExconHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; API::Node connectionNode; DataFlow::Node connectionUse; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll index 548e85e7696..049998ce8f3 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Faraday.qll @@ -23,7 +23,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * connection.get("/").body * ``` */ -class FaradayHttpRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class FaradayHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; API::Node connectionNode; DataFlow::Node connectionUse; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll index 0196d4c4d3c..255ff0277bc 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/HttpClient.qll @@ -15,7 +15,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * HTTPClient.get_content("http://example.com") * ``` */ -class HttpClientRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class HttpClientRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; API::Node connectionNode; string method; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll index 696a008e86c..99fd9e00f18 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Httparty.qll @@ -24,7 +24,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * MyClass.new("http://example.com") * ``` */ -class HttpartyRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class HttpartyRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; HttpartyRequest() { diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll index 6848f247b3f..f12f6fa8e30 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/NetHttp.qll @@ -19,7 +19,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * response = req.get("/") * ``` */ -class NetHttpRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class NetHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode { private DataFlow::CallNode request; private DataFlow::Node responseBody; private API::Node requestNode; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll index 8a519d56bf4..557c75f77ec 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/OpenURI.qll @@ -19,7 +19,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * URI.parse("http://example.com").open.read * ``` */ -class OpenUriRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class OpenUriRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; OpenUriRequest() { @@ -61,7 +61,7 @@ class OpenUriRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { * Kernel.open("http://example.com").read * ``` */ -class OpenUriKernelOpenRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class OpenUriKernelOpenRequest extends Http::Client::Request::Range, DataFlow::CallNode { OpenUriKernelOpenRequest() { this instanceof KernelMethodCall and this.getMethodName() = "open" diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll index 15ed7b49f48..6693f94585a 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/RestClient.qll @@ -17,7 +17,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * RestClient::Request.execute(url: "http://example.com").body * ``` */ -class RestClientHttpRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class RestClientHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; API::Node connectionNode; diff --git a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll index 01d10d67293..f9dcc29a266 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/http_clients/Typhoeus.qll @@ -15,7 +15,7 @@ private import codeql.ruby.dataflow.internal.DataFlowImplForHttpClientLibraries * Typhoeus.get("http://example.com").body * ``` */ -class TyphoeusHttpRequest extends HTTP::Client::Request::Range, DataFlow::CallNode { +class TyphoeusHttpRequest extends Http::Client::Request::Range, DataFlow::CallNode { API::Node requestNode; TyphoeusHttpRequest() { diff --git a/ruby/ql/lib/codeql/ruby/security/HttpToFileAccessSpecific.qll b/ruby/ql/lib/codeql/ruby/security/HttpToFileAccessSpecific.qll index 1c1ce3437b9..3cbf144d398 100644 --- a/ruby/ql/lib/codeql/ruby/security/HttpToFileAccessSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/security/HttpToFileAccessSpecific.qll @@ -12,10 +12,10 @@ private import HttpToFileAccessCustomizations::HttpToFileAccess /** * An access to a user-controlled HTTP request input, considered as a flow source for writing user-controlled data to files */ -private class RequestInputAccessAsSource extends Source instanceof HTTP::Server::RequestInputAccess { +private class RequestInputAccessAsSource extends Source instanceof Http::Server::RequestInputAccess { } /** A response from an outgoing HTTP request, considered as a flow source for writing user-controlled data to files. */ private class HttpResponseAsSource extends Source { - HttpResponseAsSource() { this = any(HTTP::Client::Request r).getResponseBody() } + HttpResponseAsSource() { this = any(Http::Client::Request r).getResponseBody() } } diff --git a/ruby/ql/lib/codeql/ruby/security/InsecureDownloadCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/InsecureDownloadCustomizations.qll index e9850445ef5..3c718655722 100644 --- a/ruby/ql/lib/codeql/ruby/security/InsecureDownloadCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/InsecureDownloadCustomizations.qll @@ -135,7 +135,7 @@ module InsecureDownload { * In other words, if the URL is HTTP and the extension is in `unsafeExtension()`. */ private class HttpResponseAsSink extends Sink { - private HTTP::Client::Request req; + private Http::Client::Request req; HttpResponseAsSink() { this = req.getAUrlPart() and @@ -155,7 +155,7 @@ module InsecureDownload { /** * Gets a node for the response from `request`, type-tracked using `t`. */ - DataFlow::LocalSourceNode clientRequestResponse(TypeTracker t, HTTP::Client::Request request) { + DataFlow::LocalSourceNode clientRequestResponse(TypeTracker t, Http::Client::Request request) { t.start() and result = request.getResponseBody() or @@ -166,7 +166,7 @@ module InsecureDownload { * A url that is downloaded through an insecure connection, where the result ends up being saved to a sensitive location. */ class FileWriteSink extends Sink { - HTTP::Client::Request request; + Http::Client::Request request; FileWriteSink() { // For example, in: diff --git a/ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll b/ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll index e11bc5182f0..65e662f0bc5 100644 --- a/ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/OverlyLargeRangeQuery.qll @@ -96,7 +96,10 @@ class OverlyWideRange extends RegExpCharacterRange { toCodePoint("A") <= high or // a non-alphanumeric char as part of the range boundaries - exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) + exists(int bound | bound = [low, high] | not isAlphanumeric(bound.toUnicode())) and + // while still being ascii + low < 128 and + high < 128 ) and // allowlist for known ranges not this = allowedWideRanges() diff --git a/ruby/ql/lib/codeql/ruby/security/ServerSideRequestForgeryCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/ServerSideRequestForgeryCustomizations.qll index 840ced98fc5..30ccee6d986 100644 --- a/ruby/ql/lib/codeql/ruby/security/ServerSideRequestForgeryCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/ServerSideRequestForgeryCustomizations.qll @@ -45,7 +45,7 @@ module ServerSideRequestForgery { /** The URL of an HTTP request, considered as a sink. */ class HttpRequestAsSink extends Sink { - HttpRequestAsSink() { exists(HTTP::Client::Request req | req.getAUrlPart() = this) } + HttpRequestAsSink() { exists(Http::Client::Request req | req.getAUrlPart() = this) } } /** A string interpolation with a fixed prefix, considered as a flow sanitizer. */ diff --git a/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll index 8fb8ada584e..998bb0ff110 100644 --- a/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/security/UrlRedirectCustomizations.qll @@ -57,7 +57,7 @@ module UrlRedirect { */ class RedirectLocationAsSink extends Sink { RedirectLocationAsSink() { - exists(HTTP::Server::HttpRedirectResponse e, MethodBase method | + exists(Http::Server::HttpRedirectResponse e, MethodBase method | this = e.getRedirectLocation() and // We only want handlers for GET requests. // Handlers for other HTTP methods are not as vulnerable to URL diff --git a/ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll b/ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll index 033b8aa8cfd..5112bdad11e 100644 --- a/ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll +++ b/ruby/ql/lib/codeql/ruby/security/regexp/NfaUtils.qll @@ -887,11 +887,10 @@ module PrefixConstruction { /** * Holds if `state` is the textually last start state for the regular expression. */ - private predicate lastStartState(State state) { + private predicate lastStartState(RelevantState state) { exists(RegExpRoot root | state = - max(State s, Location l | - s = stateInRelevantRegexp() and + max(RelevantState s, Location l | isStartState(s) and getRoot(s.getRepr()) = root and l = s.getRepr().getLocation() @@ -963,10 +962,17 @@ module PrefixConstruction { min(string c | delta(prev, any(InputSymbol symbol | c = intersect(Any(), symbol)), next)) } - /** Gets a state within a regular expression that contains a candidate state. */ - pragma[noinline] - State stateInRelevantRegexp() { - exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(result.getRepr())) + /** A state within a regular expression that contains a candidate state. */ + class RelevantState instanceof State { + RelevantState() { + exists(State s | isCandidate(s) | getRoot(s.getRepr()) = getRoot(this.getRepr())) + } + + /** Gets a string representation for this state in a regular expression. */ + string toString() { result = State.super.toString() } + + /** Gets the term represented by this state. */ + RegExpTerm getRepr() { result = State.super.getRepr() } } } @@ -1007,6 +1013,8 @@ module ReDoSPruning { import PrefixConstruction as Prefix + class RelevantState = Prefix::RelevantState; + /** * Predicates for testing the presence of a rejecting suffix. * @@ -1040,32 +1048,26 @@ module ReDoSPruning { * This predicate might find impossible suffixes when searching for suffixes of length > 1, which can cause FPs. */ pragma[noinline] - private predicate isLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and - ( - // exists a reject edge with some char. - hasRejectEdge(s) - or - hasEdgeToLikelyRejectable(s) - or - // stopping here is rejection - isRejectState(s) - ) + private predicate isLikelyRejectable(RelevantState s) { + // exists a reject edge with some char. + hasRejectEdge(s) + or + hasEdgeToLikelyRejectable(s) + or + // stopping here is rejection + isRejectState(s) } /** * Holds if `s` is not an accept state, and there is no epsilon transition to an accept state. */ - predicate isRejectState(State s) { - s = Prefix::stateInRelevantRegexp() and not epsilonSucc*(s) = Accept(_) - } + predicate isRejectState(RelevantState s) { not epsilonSucc*(s) = Accept(_) } /** * Holds if there is likely a non-empty suffix leading to rejection starting in `s`. */ pragma[noopt] - predicate hasEdgeToLikelyRejectable(State s) { - s = Prefix::stateInRelevantRegexp() and + predicate hasEdgeToLikelyRejectable(RelevantState s) { // all edges (at least one) with some char leads to another state that is rejectable. // the `next` states might not share a common suffix, which can cause FPs. exists(string char | char = hasEdgeToLikelyRejectableHelper(s) | @@ -1080,8 +1082,7 @@ module ReDoSPruning { * and `s` has not been found to be rejectable by `hasRejectEdge` or `isRejectState`. */ pragma[noinline] - private string hasEdgeToLikelyRejectableHelper(State s) { - s = Prefix::stateInRelevantRegexp() and + private string hasEdgeToLikelyRejectableHelper(RelevantState s) { not hasRejectEdge(s) and not isRejectState(s) and deltaClosedChar(s, result, _) @@ -1092,9 +1093,7 @@ module ReDoSPruning { * along epsilon edges, such that there is a transition from * `prev` to `next` that the character symbol `char`. */ - predicate deltaClosedChar(State prev, string char, State next) { - prev = Prefix::stateInRelevantRegexp() and - next = Prefix::stateInRelevantRegexp() and + predicate deltaClosedChar(RelevantState prev, string char, RelevantState next) { deltaClosed(prev, getAnInputSymbolMatchingRelevant(char), next) } diff --git a/ruby/ql/lib/ruby.dbscheme b/ruby/ql/lib/ruby.dbscheme index 4ba51641799..3595c826de6 100644 --- a/ruby/ql/lib/ruby.dbscheme +++ b/ruby/ql/lib/ruby.dbscheme @@ -247,20 +247,16 @@ ruby_binary_def( int right: @ruby_underscore_expression ref ); +ruby_block_body( + unique int ruby_block: @ruby_block ref, + unique int body: @ruby_block_body ref +); + ruby_block_parameters( unique int ruby_block: @ruby_block ref, unique int parameters: @ruby_block_parameters ref ); -@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement - -#keyset[ruby_block, index] -ruby_block_child( - int ruby_block: @ruby_block ref, - int index: int ref, - unique int child: @ruby_block_child_type ref -); - ruby_block_def( unique int id: @ruby_block ); @@ -274,6 +270,19 @@ ruby_block_argument_def( unique int id: @ruby_block_argument ); +@ruby_block_body_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block_body, index] +ruby_block_body_child( + int ruby_block_body: @ruby_block_body ref, + int index: int ref, + unique int child: @ruby_block_body_child_type ref +); + +ruby_block_body_def( + unique int id: @ruby_block_body +); + ruby_block_parameter_name( unique int ruby_block_parameter: @ruby_block_parameter ref, unique int name: @ruby_token_identifier ref @@ -303,6 +312,19 @@ ruby_block_parameters_def( unique int id: @ruby_block_parameters ); +@ruby_body_statement_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_body_statement, index] +ruby_body_statement_child( + int ruby_body_statement: @ruby_body_statement ref, + int index: int ref, + unique int child: @ruby_body_statement_child_type ref +); + +ruby_body_statement_def( + unique int id: @ruby_body_statement +); + ruby_break_child( unique int ruby_break: @ruby_break ref, unique int child: @ruby_argument_list ref @@ -391,6 +413,11 @@ ruby_chained_string_def( unique int id: @ruby_chained_string ); +ruby_class_body( + unique int ruby_class: @ruby_class ref, + unique int body: @ruby_body_statement ref +); + @ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant ruby_class_superclass( @@ -398,15 +425,6 @@ ruby_class_superclass( unique int superclass: @ruby_superclass ref ); -@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement - -#keyset[ruby_class, index] -ruby_class_child( - int ruby_class: @ruby_class ref, - int index: int ref, - unique int child: @ruby_class_child_type ref -); - ruby_class_def( unique int id: @ruby_class, int name: @ruby_class_name_type ref @@ -478,20 +496,16 @@ ruby_do_def( unique int id: @ruby_do ); +ruby_do_block_body( + unique int ruby_do_block: @ruby_do_block ref, + unique int body: @ruby_body_statement ref +); + ruby_do_block_parameters( unique int ruby_do_block: @ruby_do_block ref, unique int parameters: @ruby_block_parameters ref ); -@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement - -#keyset[ruby_do_block, index] -ruby_do_block_child( - int ruby_do_block: @ruby_do_block ref, - int index: int ref, - unique int child: @ruby_do_block_child_type ref -); - ruby_do_block_def( unique int id: @ruby_do_block ); @@ -724,7 +738,7 @@ ruby_in_clause_def( int pattern: @ruby_underscore_pattern_top_expr_body ref ); -@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_nonlocal_variable | @ruby_underscore_statement #keyset[ruby_interpolation, index] ruby_interpolation_child( @@ -797,20 +811,18 @@ ruby_left_assignment_list_def( unique int id: @ruby_left_assignment_list ); +@ruby_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_method_body( + unique int ruby_method: @ruby_method ref, + unique int body: @ruby_method_body_type ref +); + ruby_method_parameters( unique int ruby_method: @ruby_method ref, unique int parameters: @ruby_method_parameters ref ); -@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement - -#keyset[ruby_method, index] -ruby_method_child( - int ruby_method: @ruby_method ref, - int index: int ref, - unique int child: @ruby_method_child_type ref -); - ruby_method_def( unique int id: @ruby_method, int name: @ruby_underscore_method_name ref @@ -829,17 +841,13 @@ ruby_method_parameters_def( unique int id: @ruby_method_parameters ); -@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant - -@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement - -#keyset[ruby_module, index] -ruby_module_child( - int ruby_module: @ruby_module ref, - int index: int ref, - unique int child: @ruby_module_child_type ref +ruby_module_body( + unique int ruby_module: @ruby_module ref, + unique int body: @ruby_body_statement ref ); +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + ruby_module_def( unique int id: @ruby_module, int name: @ruby_module_name_type ref @@ -1074,13 +1082,9 @@ ruby_setter_def( int name: @ruby_token_identifier ref ); -@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement - -#keyset[ruby_singleton_class, index] -ruby_singleton_class_child( - int ruby_singleton_class: @ruby_singleton_class ref, - int index: int ref, - unique int child: @ruby_singleton_class_child_type ref +ruby_singleton_class_body( + unique int ruby_singleton_class: @ruby_singleton_class ref, + unique int body: @ruby_body_statement ref ); ruby_singleton_class_def( @@ -1088,6 +1092,13 @@ ruby_singleton_class_def( int value: @ruby_underscore_arg ref ); +@ruby_singleton_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_singleton_method_body( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int body: @ruby_singleton_method_body_type ref +); + @ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable ruby_singleton_method_parameters( @@ -1095,15 +1106,6 @@ ruby_singleton_method_parameters( unique int parameters: @ruby_method_parameters ref ); -@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement - -#keyset[ruby_singleton_method, index] -ruby_singleton_method_child( - int ruby_singleton_method: @ruby_singleton_method ref, - int index: int ref, - unique int child: @ruby_singleton_method_child_type ref -); - ruby_singleton_method_def( unique int id: @ruby_singleton_method, int name: @ruby_underscore_method_name ref, @@ -1344,7 +1346,7 @@ case @ruby_token.kind of ; -@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield +@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_body | @ruby_block_parameter | @ruby_block_parameters | @ruby_body_statement | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield @ruby_ast_node_parent = @file | @ruby_ast_node diff --git a/ruby/ql/lib/ruby.dbscheme.stats b/ruby/ql/lib/ruby.dbscheme.stats index 782ae4db56b..4a594923a20 100644 --- a/ruby/ql/lib/ruby.dbscheme.stats +++ b/ruby/ql/lib/ruby.dbscheme.stats @@ -5,7 +5,7 @@ @diagnostic_error - 155 + 130 @diagnostic_info @@ -21,7 +21,7 @@ @erb_directive - 1414 + 1400 @erb_graphql_directive @@ -29,19 +29,19 @@ @erb_output_directive - 3806 + 3859 @erb_reserved_word - 10441 + 10520 @erb_template - 1563 + 1540 @erb_token_code - 5220 + 5260 @erb_token_comment @@ -49,23 +49,23 @@ @erb_token_content - 3761 + 5478 @file - 17168 + 17206 @folder - 4792 + 4799 @location_default - 8508604 + 8706731 - + @ruby_alias - 1244 + 1264 @ruby_alternative_pattern @@ -73,11 +73,11 @@ @ruby_argument_list - 664517 + 670202 @ruby_array - 245561 + 246221 @ruby_array_pattern @@ -89,19 +89,19 @@ @ruby_assignment - 130246 + 131940 @ruby_bare_string - 11487 + 11622 @ruby_bare_symbol - 2106 + 2085 @ruby_begin - 2523 + 2549 @ruby_begin_block @@ -109,87 +109,87 @@ @ruby_binary_ampersand - 474 + 491 @ruby_binary_ampersandampersand - 8700 + 8705 @ruby_binary_and - 1348 + 1182 @ruby_binary_bangequal - 1586 + 1584 @ruby_binary_bangtilde - 178 + 177 @ruby_binary_caret - 155 + 157 @ruby_binary_equalequal - 31159 + 31539 @ruby_binary_equalequalequal - 603 + 631 @ruby_binary_equaltilde - 1820 + 1804 @ruby_binary_langle - 1334 + 1327 @ruby_binary_langleequal - 368 + 377 @ruby_binary_langleequalrangle - 749 + 758 @ruby_binary_langlelangle - 10368 + 10536 @ruby_binary_minus - 2381 + 2409 @ruby_binary_or - 665 + 622 @ruby_binary_percent - 1009 + 1017 @ruby_binary_pipe - 968 + 969 @ruby_binary_pipepipe - 8001 + 7935 @ruby_binary_plus - 6116 + 6173 @ruby_binary_rangle - 2400 + 2374 @ruby_binary_rangleequal - 558 + 575 @ruby_binary_ranglerangle @@ -197,43 +197,51 @@ @ruby_binary_slash - 1248 + 1257 @ruby_binary_star - 3286 + 3328 @ruby_binary_starstar - 1316 + 1332 @ruby_block - 97359 + 98296 @ruby_block_argument - 6050 + 6115 + + + @ruby_block_body + 97983 @ruby_block_parameter - 2410 + 2442 @ruby_block_parameters - 23336 + 23772 + + + @ruby_body_statement + 201560 @ruby_break - 3338 + 3355 @ruby_call - 965401 + 974897 @ruby_case__ - 1219 + 1224 @ruby_case_match @@ -241,11 +249,11 @@ @ruby_chained_string - 885 + 877 @ruby_class - 16841 + 16946 @ruby_complex @@ -253,11 +261,11 @@ @ruby_conditional - 3464 + 3495 @ruby_delimited_symbol - 1257 + 1264 @ruby_destructured_left_assignment @@ -269,23 +277,23 @@ @ruby_do - 1610 + 1632 @ruby_do_block - 137435 + 138119 @ruby_element_reference - 82234 + 82869 @ruby_else - 6933 + 7138 @ruby_elsif - 1595 + 1578 @ruby_end_block @@ -293,15 +301,15 @@ @ruby_ensure - 3718 + 3803 @ruby_exception_variable - 1001 + 991 @ruby_exceptions - 1639 + 1654 @ruby_expression_reference_pattern @@ -317,7 +325,7 @@ @ruby_hash - 39413 + 39640 @ruby_hash_pattern @@ -325,19 +333,19 @@ @ruby_hash_splat_argument - 1856 + 1857 @ruby_hash_splat_parameter - 1355 + 1447 @ruby_heredoc_body - 5575 + 5789 @ruby_if - 18392 + 18341 @ruby_if_guard @@ -345,7 +353,7 @@ @ruby_if_modifier - 13620 + 13708 @ruby_in @@ -357,11 +365,11 @@ @ruby_interpolation - 38208 + 38051 @ruby_keyword_parameter - 3794 + 3939 @ruby_keyword_pattern @@ -369,31 +377,31 @@ @ruby_lambda - 7472 + 7557 @ruby_lambda_parameters - 1664 + 1665 @ruby_left_assignment_list - 2873 + 2910 @ruby_method - 98356 + 99525 @ruby_method_parameters - 28861 + 29417 @ruby_module - 21557 + 21589 @ruby_next - 2005 + 2021 @ruby_operator_assignment_ampersandampersandequal @@ -401,7 +409,7 @@ @ruby_operator_assignment_ampersandequal - 16 + 17 @ruby_operator_assignment_caretequal @@ -413,23 +421,23 @@ @ruby_operator_assignment_minusequal - 292 + 294 @ruby_operator_assignment_percentequal - 25 + 26 @ruby_operator_assignment_pipeequal - 138 + 142 @ruby_operator_assignment_pipepipeequal - 4611 + 4545 @ruby_operator_assignment_plusequal - 1759 + 1732 @ruby_operator_assignment_ranglerangleequal @@ -437,11 +445,11 @@ @ruby_operator_assignment_slashequal - 12 + 13 @ruby_operator_assignment_starequal - 49 + 50 @ruby_operator_assignment_starstarequal @@ -449,11 +457,11 @@ @ruby_optional_parameter - 6435 + 6540 @ruby_pair - 235158 + 237637 @ruby_parenthesized_pattern @@ -461,27 +469,27 @@ @ruby_parenthesized_statements - 10247 + 10510 @ruby_pattern - 3895 + 3918 @ruby_program - 17142 + 17193 @ruby_range_dotdot - 2831 + 2976 @ruby_range_dotdotdot - 1356 + 1459 @ruby_rational - 123 + 124 @ruby_redo @@ -489,99 +497,99 @@ @ruby_regex - 12854 + 13096 @ruby_rescue - 2061 + 2085 @ruby_rescue_modifier - 525 + 519 @ruby_reserved_word - 3667249 + 3700878 @ruby_rest_assignment - 398 + 399 @ruby_retry - 56 + 59 @ruby_return - 8495 + 8486 @ruby_right_assignment_list - 1288 + 1274 @ruby_scope_resolution - 80009 + 81024 @ruby_setter - 598 + 601 @ruby_singleton_class - 620 + 635 @ruby_singleton_method - 6539 + 6692 @ruby_splat_argument - 3046 + 3093 @ruby_splat_parameter - 2905 + 2978 @ruby_string__ - 473533 + 476418 @ruby_string_array - 3840 + 3913 @ruby_subshell - 403 + 393 @ruby_superclass - 13318 + 13378 @ruby_symbol_array - 463 + 460 @ruby_then - 24329 + 24194 @ruby_token_character - 437 + 439 @ruby_token_class_variable - 857 + 858 @ruby_token_comment - 180978 + 182145 @ruby_token_constant - 283898 + 285989 @ruby_token_empty_statement @@ -593,11 +601,11 @@ @ruby_token_escape_sequence - 75715 + 76101 @ruby_token_false - 16909 + 17332 @ruby_token_file @@ -605,23 +613,23 @@ @ruby_token_float - 7830 + 7894 @ruby_token_forward_argument - 72 + 71 @ruby_token_forward_parameter - 94 + 111 @ruby_token_global_variable - 7057 + 7235 @ruby_token_hash_key_symbol - 228509 + 231030 @ruby_token_hash_splat_nil @@ -629,27 +637,27 @@ @ruby_token_heredoc_beginning - 5600 + 5789 @ruby_token_heredoc_content - 12542 + 12722 @ruby_token_heredoc_end - 5575 + 5789 @ruby_token_identifier - 1483080 + 1498233 @ruby_token_instance_variable - 81049 + 84088 @ruby_token_integer - 298658 + 300415 @ruby_token_line @@ -657,31 +665,31 @@ @ruby_token_nil - 17920 + 18114 @ruby_token_operator - 781 + 803 @ruby_token_self - 12958 + 13236 @ruby_token_simple_symbol - 249119 + 252789 @ruby_token_string_content - 488016 + 491238 @ruby_token_super - 5058 + 5049 @ruby_token_true - 23298 + 23791 @ruby_token_uninterpreted @@ -689,35 +697,35 @@ @ruby_unary_bang - 5788 + 5826 @ruby_unary_definedquestion - 1312 + 1320 @ruby_unary_minus - 9122 + 9472 @ruby_unary_not - 236 + 190 @ruby_unary_plus - 1386 + 1416 @ruby_unary_tilde - 88 + 89 @ruby_undef - 180 + 181 @ruby_unless - 2578 + 2594 @ruby_unless_guard @@ -725,15 +733,15 @@ @ruby_unless_modifier - 4207 + 4163 @ruby_until - 113 + 121 @ruby_until_modifier - 206 + 223 @ruby_variable_reference_pattern @@ -741,32 +749,32 @@ @ruby_when - 3239 + 3248 @ruby_while - 1335 + 1349 @ruby_while_modifier - 179 + 190 @ruby_yield - 2385 + 2389 containerparent - 21934 + 21979 parent - 4792 + 4799 child - 21934 + 21979 @@ -780,37 +788,37 @@ 1 2 - 2142 + 2145 2 3 - 909 + 910 3 4 - 441 + 442 4 5 - 298 + 299 5 7 - 376 + 377 7 13 - 389 + 390 13 124 - 233 + 234 @@ -826,7 +834,7 @@ 1 2 - 21934 + 21979 @@ -836,31 +844,31 @@ diagnostics - 155 + 130 id - 155 + 130 severity - 12 + 13 error_tag - 12 + 13 error_message - 25 + 26 full_error_message - 129 + 104 location - 155 + 130 @@ -874,7 +882,7 @@ 1 2 - 155 + 130 @@ -890,7 +898,7 @@ 1 2 - 155 + 130 @@ -906,7 +914,7 @@ 1 2 - 155 + 130 @@ -922,7 +930,7 @@ 1 2 - 155 + 130 @@ -938,7 +946,7 @@ 1 2 - 155 + 130 @@ -952,9 +960,9 @@ 12 - 12 - 13 - 12 + 10 + 11 + 13 @@ -970,7 +978,7 @@ 1 2 - 12 + 13 @@ -986,7 +994,7 @@ 2 3 - 12 + 13 @@ -1000,9 +1008,9 @@ 12 - 10 - 11 - 12 + 8 + 9 + 13 @@ -1016,9 +1024,9 @@ 12 - 12 - 13 - 12 + 10 + 11 + 13 @@ -1032,9 +1040,9 @@ 12 - 12 - 13 - 12 + 10 + 11 + 13 @@ -1050,7 +1058,7 @@ 1 2 - 12 + 13 @@ -1066,7 +1074,7 @@ 2 3 - 12 + 13 @@ -1080,9 +1088,9 @@ 12 - 10 - 11 - 12 + 8 + 9 + 13 @@ -1096,9 +1104,9 @@ 12 - 12 - 13 - 12 + 10 + 11 + 13 @@ -1114,65 +1122,65 @@ 1 2 - 12 - - - 11 - 12 - 12 - - - - - - - error_message - severity - - - 12 - - - 1 - 2 - 25 - - - - - - - error_message - error_tag - - - 12 - - - 1 - 2 - 25 - - - - - - - error_message - full_error_message - - - 12 - - - 1 - 2 - 12 + 13 9 10 - 12 + 13 + + + + + + + error_message + severity + + + 12 + + + 1 + 2 + 26 + + + + + + + error_message + error_tag + + + 12 + + + 1 + 2 + 26 + + + + + + + error_message + full_error_message + + + 12 + + + 1 + 2 + 13 + + + 7 + 8 + 13 @@ -1188,12 +1196,12 @@ 1 2 - 12 + 13 - 11 - 12 - 12 + 9 + 10 + 13 @@ -1209,12 +1217,12 @@ 1 2 - 103 + 78 2 3 - 25 + 26 @@ -1230,7 +1238,7 @@ 1 2 - 129 + 104 @@ -1246,7 +1254,7 @@ 1 2 - 129 + 104 @@ -1262,7 +1270,7 @@ 1 2 - 129 + 104 @@ -1278,12 +1286,12 @@ 1 2 - 103 + 78 2 3 - 25 + 26 @@ -1299,7 +1307,7 @@ 1 2 - 155 + 130 @@ -1315,7 +1323,7 @@ 1 2 - 155 + 130 @@ -1331,7 +1339,7 @@ 1 2 - 155 + 130 @@ -1347,7 +1355,7 @@ 1 2 - 155 + 130 @@ -1363,7 +1371,7 @@ 1 2 - 155 + 130 @@ -1373,23 +1381,23 @@ erb_ast_node_info - 25078 + 26945 node - 25078 + 26945 parent - 6088 + 6111 parent_index - 668 + 688 loc - 25075 + 26942 @@ -1403,7 +1411,7 @@ 1 2 - 25078 + 26945 @@ -1419,7 +1427,7 @@ 1 2 - 25078 + 26945 @@ -1435,7 +1443,7 @@ 1 2 - 25078 + 26945 @@ -1451,17 +1459,17 @@ 1 3 - 460 + 446 3 4 - 5360 + 5403 4 - 226 - 267 + 237 + 262 @@ -1477,17 +1485,17 @@ 1 3 - 460 + 446 3 4 - 5360 + 5403 4 - 226 - 267 + 237 + 262 @@ -1503,17 +1511,17 @@ 1 3 - 460 + 446 3 4 - 5360 + 5403 4 - 226 - 267 + 237 + 262 @@ -1529,57 +1537,62 @@ 1 2 - 86 + 2 2 3 - 106 + 134 3 4 - 121 + 43 4 5 - 5 + 75 5 6 - 65 + 61 6 7 - 50 + 70 7 - 11 - 50 + 9 + 61 - 11 - 19 - 53 + 10 + 16 + 55 - 19 - 38 - 53 + 16 + 23 + 52 - 39 - 72 - 50 + 23 + 43 + 52 - 74 - 2050 - 23 + 45 + 71 + 52 + + + 73 + 2096 + 26 @@ -1595,57 +1608,62 @@ 1 2 - 86 + 2 2 3 - 106 + 134 3 4 - 121 + 43 4 5 - 5 + 75 5 6 - 65 + 61 6 7 - 50 + 70 7 - 11 - 50 + 9 + 61 - 11 - 19 - 53 + 10 + 16 + 55 - 19 - 38 - 53 + 16 + 23 + 52 - 39 - 72 - 50 + 23 + 43 + 52 - 74 - 2050 - 23 + 45 + 71 + 52 + + + 73 + 2096 + 26 @@ -1661,57 +1679,62 @@ 1 2 - 86 + 2 2 3 - 106 + 134 3 4 - 121 + 43 4 5 - 5 + 75 5 6 - 65 + 61 6 7 - 50 + 70 7 - 11 - 50 + 9 + 61 - 11 - 19 - 53 + 10 + 16 + 55 - 19 - 38 - 53 + 16 + 23 + 52 - 39 - 72 - 50 + 23 + 43 + 52 - 74 - 2049 - 23 + 45 + 71 + 52 + + + 73 + 2095 + 26 @@ -1727,7 +1750,7 @@ 1 2 - 25072 + 26939 2 @@ -1748,7 +1771,7 @@ 1 2 - 25072 + 26939 2 @@ -1769,7 +1792,7 @@ 1 2 - 25075 + 26942 @@ -1778,11 +1801,11 @@ - erb_comment_directive_def + erb_comment_directive_child 40 - id + erb_comment_directive 40 @@ -1792,7 +1815,7 @@ - id + erb_comment_directive child @@ -1809,7 +1832,7 @@ child - id + erb_comment_directive 12 @@ -1826,21 +1849,32 @@ - erb_directive_def - 1414 + erb_comment_directive_def + 40 id - 1414 + 40 + + + + + + erb_directive_child + 1400 + + + erb_directive + 1400 child - 1414 + 1400 - id + erb_directive child @@ -1849,7 +1883,7 @@ 1 2 - 1414 + 1400 @@ -1857,7 +1891,7 @@ child - id + erb_directive 12 @@ -1865,7 +1899,66 @@ 1 2 - 1414 + 1400 + + + + + + + + + erb_directive_def + 1400 + + + id + 1400 + + + + + + erb_graphql_directive_child + 0 + + + erb_graphql_directive + 0 + + + child + 0 + + + + + erb_graphql_directive + child + + + 12 + + + 1 + 2 + 2 + + + + + + + child + erb_graphql_directive + + + 12 + + + 1 + 2 + 2 @@ -1881,14 +1974,291 @@ id 0 + + + + + erb_output_directive_child + 3859 + + + erb_output_directive + 3859 + child - 0 + 3859 - id + erb_output_directive + child + + + 12 + + + 1 + 2 + 3859 + + + + + + + child + erb_output_directive + + + 12 + + + 1 + 2 + 3859 + + + + + + + + + erb_output_directive_def + 3859 + + + id + 3859 + + + + + + erb_template_child + 10739 + + + erb_template + 425 + + + index + 688 + + + child + 10739 + + + + + erb_template + index + + + 12 + + + 1 + 3 + 20 + + + 3 + 4 + 142 + + + 4 + 7 + 26 + + + 7 + 10 + 32 + + + 10 + 14 + 32 + + + 14 + 24 + 32 + + + 24 + 32 + 35 + + + 33 + 42 + 35 + + + 43 + 68 + 32 + + + 70 + 190 + 32 + + + 235 + 237 + 5 + + + + + + + erb_template + child + + + 12 + + + 1 + 3 + 20 + + + 3 + 4 + 142 + + + 4 + 7 + 26 + + + 7 + 10 + 32 + + + 10 + 14 + 32 + + + 14 + 24 + 32 + + + 24 + 32 + 35 + + + 33 + 42 + 35 + + + 43 + 68 + 32 + + + 70 + 190 + 32 + + + 235 + 237 + 5 + + + + + + + index + erb_template + + + 12 + + + 1 + 2 + 2 + + + 2 + 3 + 134 + + + 3 + 4 + 43 + + + 4 + 5 + 75 + + + 5 + 6 + 61 + + + 6 + 7 + 70 + + + 7 + 9 + 61 + + + 10 + 16 + 55 + + + 16 + 23 + 52 + + + 23 + 43 + 52 + + + 45 + 71 + 52 + + + 73 + 147 + 26 + + + + + + + index child @@ -1899,337 +2269,60 @@ 2 2 - - - - - - child - id - - - 12 - - - - - - - - erb_output_directive_def - 3806 - - - id - 3806 - - - child - 3806 - - - - - id - child - - - 12 - - - 1 - 2 - 3806 - - - - - - - child - id - - - 12 - - - 1 - 2 - 3806 - - - - - - - - - erb_template_child - 8982 - - - erb_template - 433 - - - index - 668 - - - child - 8982 - - - - - erb_template - index - - - 12 - - - 1 - 3 - 26 - - - 3 - 4 - 139 - - - 4 - 7 - 26 - - - 7 - 9 - 29 - - - 9 - 12 - 35 - - - 12 - 17 - 38 - - - 17 - 29 - 32 - - - 29 - 35 - 35 - - - 35 - 56 - 35 - - - 61 - 226 - 32 - - - - - - - erb_template - child - - - 12 - - - 1 - 3 - 26 - - - 3 - 4 - 139 - - - 4 - 7 - 26 - - - 7 - 9 - 29 - - - 9 - 12 - 35 - - - 12 - 17 - 38 - - - 17 - 29 - 32 - - - 29 - 35 - 35 - - - 35 - 56 - 35 - - - 61 - 226 - 32 - - - - - - - index - erb_template - - - 12 - - - 1 - 2 - 86 - 2 3 - 106 + 134 3 4 - 121 + 43 4 5 - 5 + 75 5 6 - 65 + 61 6 7 - 50 + 70 7 - 11 - 50 + 9 + 61 - 11 - 19 - 53 + 10 + 16 + 55 - 19 - 38 - 53 + 16 + 23 + 52 - 39 - 72 - 50 + 23 + 43 + 52 - 74 + 45 + 71 + 52 + + + 73 147 - 23 - - - - - - - index - child - - - 12 - - - 1 - 2 - 86 - - - 2 - 3 - 106 - - - 3 - 4 - 121 - - - 4 - 5 - 5 - - - 5 - 6 - 65 - - - 6 - 7 - 50 - - - 7 - 11 - 50 - - - 11 - 19 - 53 - - - 19 - 38 - 53 - - - 39 - 72 - 50 - - - 74 - 147 - 23 + 26 @@ -2245,7 +2338,7 @@ 1 2 - 8982 + 10739 @@ -2261,7 +2354,7 @@ 1 2 - 8982 + 10739 @@ -2271,22 +2364,22 @@ erb_template_def - 1563 + 1540 id - 1563 + 1540 erb_tokeninfo - 19423 + 21259 id - 19423 + 21259 kind @@ -2294,7 +2387,7 @@ value - 5800 + 5773 @@ -2308,7 +2401,7 @@ 1 2 - 19423 + 21259 @@ -2324,7 +2417,7 @@ 1 2 - 19423 + 21259 @@ -2338,18 +2431,18 @@ 12 - 1266 - 1267 + 1803 + 1804 2 - 1757 - 1758 + 1878 + 1879 2 - 3514 - 3515 + 3606 + 3607 2 @@ -2364,18 +2457,18 @@ 12 - 52 - 53 + 5 + 6 2 - 884 - 885 + 932 + 933 2 - 1016 - 1017 + 1042 + 1043 2 @@ -2392,22 +2485,17 @@ 1 2 - 4662 + 4667 2 3 - 659 + 679 3 - 23 - 436 - - - 32 - 1696 - 41 + 1742 + 425 @@ -2423,7 +2511,7 @@ 1 2 - 5800 + 5773 @@ -2433,15 +2521,15 @@ files - 17168 + 17206 id - 17168 + 17206 name - 17168 + 17206 @@ -2455,7 +2543,7 @@ 1 2 - 17168 + 17206 @@ -2471,7 +2559,7 @@ 1 2 - 17168 + 17206 @@ -2481,15 +2569,15 @@ folders - 4792 + 4799 id - 4792 + 4799 name - 4792 + 4799 @@ -2503,7 +2591,7 @@ 1 2 - 4792 + 4799 @@ -2519,7 +2607,7 @@ 1 2 - 4792 + 4799 @@ -2529,31 +2617,31 @@ locations_default - 8508604 + 8706731 id - 8508604 + 8706731 file - 17168 + 17206 start_line - 30674 + 30719 start_column - 5064 + 5072 end_line - 30674 + 30719 end_column - 5129 + 5163 @@ -2567,7 +2655,7 @@ 1 2 - 8508604 + 8706731 @@ -2583,7 +2671,7 @@ 1 2 - 8508604 + 8706731 @@ -2599,7 +2687,7 @@ 1 2 - 8508604 + 8706731 @@ -2615,7 +2703,7 @@ 1 2 - 8508604 + 8706731 @@ -2631,7 +2719,7 @@ 1 2 - 8508604 + 8706731 @@ -2647,67 +2735,67 @@ 1 32 - 1337 + 1300 32 - 48 - 1311 + 49 + 1339 - 48 - 72 - 1376 + 49 + 73 + 1482 - 72 - 94 - 1493 + 73 + 95 + 1352 - 94 - 127 - 1298 + 95 + 126 + 1313 127 - 169 - 1311 + 170 + 1313 - 169 - 216 - 1298 + 170 + 218 + 1339 - 216 - 271 - 1298 + 218 + 277 + 1326 - 271 - 353 - 1311 + 277 + 359 + 1300 - 353 - 475 - 1298 + 361 + 489 + 1300 - 476 - 721 - 1298 + 492 + 736 + 1313 - 724 - 1480 - 1298 + 741 + 1508 + 1300 - 1481 - 22817 - 1233 + 1509 + 22819 + 1222 @@ -2716,158 +2804,6 @@ file start_line - - - 12 - - - 1 - 7 - 1116 - - - 7 - 10 - 1545 - - - 10 - 13 - 1389 - - - 13 - 16 - 1493 - - - 16 - 20 - 1571 - - - 20 - 25 - 1363 - - - 25 - 31 - 1441 - - - 31 - 38 - 1350 - - - 38 - 49 - 1441 - - - 49 - 69 - 1324 - - - 69 - 117 - 1298 - - - 119 - 257 - 1298 - - - 260 - 2337 - 532 - - - - - - - file - start_column - - - 12 - - - 1 - 16 - 1415 - - - 16 - 25 - 1428 - - - 25 - 32 - 1363 - - - 32 - 41 - 1376 - - - 41 - 47 - 1506 - - - 47 - 54 - 1532 - - - 54 - 62 - 1363 - - - 62 - 69 - 1402 - - - 69 - 77 - 1428 - - - 77 - 87 - 1350 - - - 87 - 101 - 1324 - - - 101 - 130 - 1298 - - - 130 - 357 - 376 - - - - - - - file - end_line 12 @@ -2875,67 +2811,219 @@ 1 8 - 1558 + 1586 8 11 - 1467 + 1443 11 14 - 1506 + 1521 14 17 - 1415 + 1430 17 21 - 1467 + 1417 21 26 - 1337 + 1352 26 32 - 1363 + 1378 32 39 - 1415 + 1378 39 51 - 1402 + 1417 51 75 - 1298 + 1326 75 126 - 1311 + 1313 126 343 - 1298 + 1300 354 2337 - 324 + 338 + + + + + + + file + start_column + + + 12 + + + 1 + 16 + 1404 + + + 16 + 25 + 1417 + + + 25 + 32 + 1378 + + + 32 + 41 + 1391 + + + 41 + 47 + 1495 + + + 47 + 54 + 1547 + + + 54 + 62 + 1339 + + + 62 + 69 + 1391 + + + 69 + 77 + 1443 + + + 77 + 87 + 1365 + + + 87 + 101 + 1326 + + + 101 + 129 + 1313 + + + 129 + 357 + 390 + + + + + + + file + end_line + + + 12 + + + 1 + 8 + 1547 + + + 8 + 11 + 1430 + + + 11 + 14 + 1547 + + + 14 + 17 + 1430 + + + 17 + 21 + 1430 + + + 21 + 26 + 1365 + + + 26 + 32 + 1378 + + + 32 + 39 + 1378 + + + 39 + 51 + 1417 + + + 51 + 75 + 1326 + + + 75 + 126 + 1313 + + + 126 + 343 + 1300 + + + 354 + 2337 + 338 @@ -2951,67 +3039,67 @@ 1 20 - 1441 + 1430 20 28 - 1298 + 1300 28 36 - 1389 + 1404 36 45 - 1363 + 1365 45 50 - 1311 + 1313 50 57 - 1350 + 1352 57 64 - 1376 + 1339 64 71 - 1298 + 1300 71 78 - 1376 + 1391 78 87 - 1428 + 1443 87 99 - 1402 + 1417 99 120 - 1363 + 1365 120 367 - 766 + 780 @@ -3027,67 +3115,72 @@ 1 2 - 1519 + 1430 2 4 - 1753 + 1716 5 6 - 3545 + 3316 6 10 - 2454 + 2523 10 - 17 - 2805 + 16 + 2367 - 17 - 24 - 2441 + 16 + 22 + 2562 - 24 - 42 - 2350 + 22 + 37 + 2327 - 42 - 78 - 2337 + 37 + 68 + 2341 - 78 - 119 - 2311 + 68 + 110 + 2314 - 119 - 173 - 2324 + 110 + 161 + 2314 - 173 - 279 - 2311 + 161 + 249 + 2314 - 281 - 866 - 2311 + 250 + 596 + 2314 - 866 - 10012 - 2207 + 601 + 3089 + 2314 + + + 3257 + 10293 + 559 @@ -3103,47 +3196,47 @@ 1 2 - 10713 + 9884 2 3 - 4753 + 5605 3 6 - 2337 + 2341 6 9 - 2363 + 2341 9 14 - 2792 + 2419 14 22 - 2324 + 2484 22 - 57 - 2311 + 49 + 2327 - 57 - 287 - 2311 + 49 + 220 + 2314 - 291 - 1322 - 766 + 223 + 1323 + 1001 @@ -3159,72 +3252,72 @@ 1 2 - 1519 + 1430 2 3 - 1584 + 1495 3 4 - 2558 + 2510 4 6 - 2441 + 2419 6 8 - 1584 + 1573 8 - 13 - 2818 + 12 + 2393 - 13 - 18 - 2610 + 12 + 16 + 2432 - 18 - 30 - 2506 + 16 + 25 + 2549 - 30 - 43 - 2363 + 25 + 39 + 2314 - 43 - 57 - 2428 + 39 + 53 + 2406 - 57 - 70 - 2324 + 53 + 66 + 2314 - 70 - 89 - 2350 + 66 + 81 + 2314 - 89 - 116 - 2311 + 81 + 104 + 2341 - 116 - 203 - 1272 + 104 + 205 + 2223 @@ -3240,42 +3333,42 @@ 1 2 - 12648 + 11106 2 3 - 6714 + 6294 3 4 - 2363 + 2275 4 5 - 1792 + 1547 5 7 - 2324 + 2653 7 - 12 - 2337 + 10 + 2432 - 12 - 34 - 2311 + 10 + 18 + 2536 - 40 + 18 241 - 181 + 1872 @@ -3291,67 +3384,67 @@ 1 2 - 1519 + 1430 2 4 - 1766 + 1742 4 5 - 3636 + 3407 5 8 - 2662 + 2757 8 13 - 2792 + 2822 13 - 17 - 2311 + 18 + 2562 - 17 + 18 28 - 2376 + 2445 28 - 42 - 2428 + 44 + 2367 - 42 - 57 - 2415 + 44 + 59 + 2419 - 57 - 71 - 2389 + 59 + 72 + 2367 - 71 + 72 88 - 2363 + 2341 88 - 113 - 2311 + 114 + 2314 - 113 - 203 - 1701 + 114 + 208 + 1742 @@ -3367,72 +3460,72 @@ 1 2 - 441 + 442 2 3 - 623 + 585 3 4 - 233 + 247 4 5 - 272 + 247 5 6 - 246 + 273 6 9 - 467 + 455 9 - 16 - 415 + 15 + 403 - 16 - 41 - 389 + 15 + 37 + 390 - 46 - 172 - 389 + 37 + 159 + 390 - 181 - 773 - 389 + 159 + 700 + 390 - 791 - 2919 - 389 + 723 + 2453 + 390 - 2920 - 8125 - 389 + 2462 + 7702 + 390 - 8227 - 25706 - 389 + 7795 + 14906 + 390 - 33875 - 34983 - 25 + 16424 + 38206 + 78 @@ -3448,52 +3541,52 @@ 1 2 - 1506 + 1456 2 3 - 545 + 481 3 4 - 428 + 481 4 - 10 - 389 + 9 + 390 - 10 - 39 - 389 + 9 + 33 + 390 - 39 - 139 - 389 + 34 + 109 + 390 - 147 - 387 - 389 + 112 + 353 + 390 - 397 - 748 - 389 + 358 + 696 + 390 - 769 - 963 - 389 + 698 + 950 + 390 - 964 - 1322 - 246 + 950 + 1323 + 312 @@ -3509,62 +3602,62 @@ 1 2 - 545 + 533 2 3 - 675 + 637 3 4 - 337 + 351 4 6 - 402 + 416 6 9 - 467 + 455 9 - 19 - 389 + 17 + 403 - 19 - 64 - 389 + 18 + 63 + 390 - 66 - 184 - 402 + 64 + 158 + 390 - 201 - 390 - 389 + 171 + 379 + 390 - 418 - 742 - 389 + 380 + 735 + 390 - 752 - 1025 - 389 + 735 + 1008 + 390 - 1028 - 1382 - 285 + 1010 + 1392 + 325 @@ -3580,62 +3673,62 @@ 1 2 - 545 + 533 2 3 - 675 + 637 3 4 - 337 + 351 4 6 - 402 + 416 6 9 - 467 + 455 9 - 19 - 389 + 17 + 403 - 19 + 18 64 - 389 + 390 - 66 - 184 - 389 + 64 + 159 + 390 - 184 + 171 381 - 389 + 390 - 396 - 752 - 389 + 385 + 749 + 390 - 753 - 1034 - 402 + 750 + 1026 + 390 - 1050 - 1386 - 285 + 1027 + 1399 + 325 @@ -3651,52 +3744,52 @@ 1 2 - 1285 + 1274 2 3 - 727 + 728 3 4 - 441 + 403 4 6 - 415 + 429 6 - 16 - 389 + 14 + 390 - 16 - 37 - 389 + 14 + 35 + 403 - 37 - 67 - 402 + 35 + 66 + 429 67 - 103 - 389 + 104 + 390 104 127 - 402 + 403 - 127 - 177 - 220 + 128 + 178 + 221 @@ -3712,72 +3805,72 @@ 1 2 - 311 + 286 3 4 - 3532 + 3316 4 6 - 2636 + 2458 6 9 - 2415 + 2497 9 14 - 2350 + 2471 14 21 - 2467 + 2393 21 - 34 - 2324 + 32 + 2341 - 34 - 64 - 2311 + 32 + 60 + 2367 - 64 - 103 - 2337 + 60 + 101 + 2314 - 103 + 101 152 - 2311 + 2354 152 230 - 2311 + 2341 230 - 525 - 2311 + 480 + 2314 - 528 - 2539 - 2311 + 482 + 2152 + 2314 - 2543 - 9864 - 740 + 2200 + 9921 + 949 @@ -3793,47 +3886,47 @@ 1 2 - 10713 + 9884 2 3 - 4753 + 5605 3 6 - 2337 + 2341 6 9 - 2363 + 2341 9 14 - 2792 + 2419 14 22 - 2324 + 2484 22 - 57 - 2311 + 49 + 2327 - 57 - 287 - 2311 + 49 + 220 + 2314 - 291 - 1306 - 766 + 223 + 1307 + 1001 @@ -3849,42 +3942,42 @@ 1 2 - 12428 + 11327 2 3 - 6532 + 5839 3 4 - 2532 + 2197 4 - 5 - 1753 + 6 + 2835 - 5 - 7 - 2207 + 6 + 8 + 2132 - 7 + 8 12 - 2441 + 2419 12 - 27 - 2363 + 24 + 2445 - 27 - 36 - 415 + 24 + 42 + 1521 @@ -3900,67 +3993,67 @@ 1 3 - 1519 + 1430 3 4 - 3688 + 3446 4 6 - 2714 + 2757 6 8 - 1649 + 1729 8 - 13 - 2766 + 12 + 2354 - 13 - 17 - 2363 + 12 + 16 + 2354 - 17 - 28 - 2415 + 16 + 24 + 2406 - 28 - 42 - 2415 + 24 + 39 + 2458 - 42 - 55 - 2337 + 39 + 53 + 2445 - 55 - 69 - 2441 + 53 + 67 + 2432 - 69 - 86 - 2415 + 67 + 82 + 2393 - 86 - 112 - 2376 + 82 + 105 + 2327 - 112 - 200 - 1571 + 105 + 204 + 2184 @@ -3976,72 +4069,72 @@ 1 2 - 1506 + 1417 2 3 - 1623 + 1534 3 4 - 2558 + 2510 4 6 - 2402 + 2367 6 8 - 1649 + 1690 8 13 - 2688 + 2783 13 18 - 2636 + 2549 18 - 30 - 2402 + 28 + 2432 - 30 + 28 44 - 2389 + 2445 44 - 58 - 2337 + 59 + 2393 - 58 + 59 72 - 2324 + 2419 72 90 - 2389 + 2354 90 117 - 2311 + 2327 117 - 202 - 1454 + 208 + 1495 @@ -4057,72 +4150,67 @@ 1 2 - 350 + 338 2 3 - 480 + 468 3 5 - 402 + 455 5 - 6 - 194 + 7 + 455 - 6 - 8 - 467 + 7 + 10 + 403 - 8 - 14 - 389 + 10 + 19 + 442 - 14 - 28 - 389 + 19 + 50 + 403 - 28 - 68 - 389 + 53 + 168 + 390 - 69 - 258 - 389 + 169 + 691 + 390 - 264 - 1063 - 389 + 806 + 2495 + 390 - 1113 - 3378 - 389 + 2618 + 6562 + 390 - 3687 - 8035 - 389 + 6710 + 9993 + 390 - 8096 - 10367 - 389 - - - 10416 - 17990 - 116 + 10241 + 19364 + 247 @@ -4138,52 +4226,52 @@ 1 2 - 1441 + 1404 2 3 - 610 + 585 3 4 - 428 + 429 4 - 9 - 402 + 7 + 390 - 9 - 41 - 389 + 7 + 24 + 390 - 42 - 123 - 389 + 24 + 94 + 390 - 143 - 406 - 389 + 95 + 314 + 390 - 406 - 760 - 389 + 325 + 676 + 390 - 771 - 994 - 389 + 688 + 977 + 390 - 1006 - 1289 - 298 + 979 + 1290 + 403 @@ -4199,67 +4287,67 @@ 1 2 - 519 + 507 2 3 - 675 + 650 3 4 - 298 + 338 4 6 - 402 + 429 6 9 - 454 + 429 9 - 16 - 415 + 15 + 390 16 39 - 389 + 429 - 39 - 136 - 389 + 40 + 138 + 390 - 136 - 344 - 389 + 141 + 332 + 390 - 353 - 637 - 389 + 352 + 653 + 390 - 675 - 1011 - 389 + 657 + 998 + 390 1011 - 1272 - 389 + 1264 + 390 - 1284 - 1387 - 25 + 1281 + 1401 + 39 @@ -4275,62 +4363,62 @@ 1 2 - 896 + 884 2 3 - 311 + 338 3 4 - 506 + 520 4 5 - 350 + 325 5 8 - 441 + 416 8 - 18 - 389 + 17 + 403 - 19 + 17 33 - 389 + 429 33 49 - 389 + 390 49 64 - 402 + 390 64 - 82 - 428 + 81 + 403 - 82 + 81 95 - 402 + 429 95 - 112 - 220 + 108 + 234 @@ -4346,67 +4434,67 @@ 1 2 - 519 + 507 2 3 - 675 + 663 3 4 - 298 + 338 4 6 - 402 + 416 6 9 - 454 + 429 9 - 16 - 415 + 15 + 403 - 16 - 39 - 402 + 15 + 36 + 390 - 48 - 141 - 389 + 37 + 131 + 390 - 142 - 352 - 389 + 133 + 314 + 390 - 359 - 637 - 389 + 314 + 632 + 390 - 674 - 999 - 389 + 637 + 969 + 390 - 1010 - 1283 - 389 + 970 + 1181 + 390 - 1381 - 1382 - 12 + 1211 + 1394 + 65 @@ -4416,19 +4504,19 @@ ruby_alias_def - 1244 + 1264 id - 1244 + 1264 alias - 1244 + 1264 name - 1244 + 1264 @@ -4442,7 +4530,7 @@ 1 2 - 1244 + 1264 @@ -4458,7 +4546,7 @@ 1 2 - 1244 + 1264 @@ -4474,7 +4562,7 @@ 1 2 - 1244 + 1264 @@ -4490,7 +4578,7 @@ 1 2 - 1244 + 1264 @@ -4506,7 +4594,7 @@ 1 2 - 1244 + 1264 @@ -4522,7 +4610,7 @@ 1 2 - 1244 + 1264 @@ -4635,19 +4723,19 @@ ruby_argument_list_child - 823783 + 832018 ruby_argument_list - 664257 + 669942 index - 428 + 429 child - 823783 + 832018 @@ -4661,17 +4749,17 @@ 1 2 - 562976 + 567211 2 3 - 64088 + 64884 3 34 - 37193 + 37846 @@ -4687,17 +4775,17 @@ 1 2 - 562976 + 567211 2 3 - 64088 + 64884 3 34 - 37193 + 37846 @@ -4713,47 +4801,47 @@ 1 2 - 142 + 143 2 3 - 38 + 39 3 7 - 38 + 39 7 11 - 38 + 39 11 - 20 - 38 + 21 + 39 - 22 - 42 - 38 + 23 + 43 + 39 - 55 - 372 - 38 + 56 + 378 + 39 - 900 - 7800 - 38 + 919 + 7900 + 39 - 51150 - 51151 - 12 + 51512 + 51513 + 13 @@ -4769,47 +4857,47 @@ 1 2 - 142 + 143 2 3 - 38 + 39 3 7 - 38 + 39 7 11 - 38 + 39 11 - 20 - 38 + 21 + 39 - 22 - 42 - 38 + 23 + 43 + 39 - 55 - 372 - 38 + 56 + 378 + 39 - 900 - 7800 - 38 + 919 + 7900 + 39 - 51150 - 51151 - 12 + 51512 + 51513 + 13 @@ -4825,7 +4913,7 @@ 1 2 - 823783 + 832018 @@ -4841,7 +4929,7 @@ 1 2 - 823783 + 832018 @@ -4851,22 +4939,22 @@ ruby_argument_list_def - 664517 + 670202 id - 664517 + 670202 ruby_array_child - 699082 + 700387 ruby_array - 237416 + 237942 index @@ -4874,7 +4962,7 @@ child - 699082 + 700387 @@ -4888,17 +4976,17 @@ 1 2 - 11987 + 12128 2 3 - 212081 + 212308 3 63361 - 13348 + 13506 @@ -4914,17 +5002,17 @@ 1 2 - 11987 + 12128 2 3 - 212081 + 212308 3 63361 - 13348 + 13506 @@ -4964,7 +5052,7 @@ 11 - 237417 + 237943 1294 @@ -5005,7 +5093,7 @@ 11 - 237417 + 237943 1294 @@ -5022,7 +5110,7 @@ 1 2 - 699082 + 700387 @@ -5038,7 +5126,7 @@ 1 2 - 699082 + 700387 @@ -5048,11 +5136,11 @@ ruby_array_def - 245561 + 246221 id - 245561 + 246221 @@ -5302,19 +5390,19 @@ ruby_assignment_def - 130246 + 131940 id - 130246 + 131940 left - 130246 + 131940 right - 130246 + 131940 @@ -5328,7 +5416,7 @@ 1 2 - 130246 + 131940 @@ -5344,7 +5432,7 @@ 1 2 - 130246 + 131940 @@ -5360,7 +5448,7 @@ 1 2 - 130246 + 131940 @@ -5376,7 +5464,7 @@ 1 2 - 130246 + 131940 @@ -5392,7 +5480,7 @@ 1 2 - 130246 + 131940 @@ -5408,7 +5496,7 @@ 1 2 - 130246 + 131940 @@ -5418,23 +5506,23 @@ ruby_ast_node_info - 8791047 + 9180290 node - 8791047 + 9180290 parent - 2884450 + 3212205 parent_index - 2792 + 2796 loc - 8496293 + 8693375 @@ -5448,7 +5536,7 @@ 1 2 - 8791047 + 9180290 @@ -5464,7 +5552,7 @@ 1 2 - 8791047 + 9180290 @@ -5480,7 +5568,7 @@ 1 2 - 8791047 + 9180290 @@ -5496,27 +5584,27 @@ 1 2 - 325272 + 517803 2 3 - 393074 + 442358 3 4 - 1608542 + 1733980 4 5 - 344466 + 339055 5 216 - 213094 + 179008 @@ -5532,27 +5620,27 @@ 1 2 - 325272 + 517803 2 3 - 393074 + 442358 3 4 - 1608542 + 1733980 4 5 - 344466 + 339055 5 216 - 213094 + 179008 @@ -5568,27 +5656,27 @@ 1 2 - 325272 + 517803 2 3 - 393074 + 442358 3 4 - 1608542 + 1733980 4 5 - 344466 + 339055 5 216 - 213094 + 179008 @@ -5604,52 +5692,57 @@ 1 2 - 649 + 455 2 3 - 389 + 195 3 - 5 - 233 + 4 + 390 - 5 + 4 6 - 233 + 234 6 - 9 - 233 + 7 + 234 - 9 + 7 + 10 + 234 + + + 10 21 - 246 + 208 21 - 48 - 220 + 42 + 221 - 48 - 127 - 220 + 43 + 95 + 221 - 130 - 946 - 220 + 99 + 491 + 221 - 1416 - 222113 - 142 + 524 + 246988 + 182 @@ -5665,52 +5758,57 @@ 1 2 - 649 + 455 2 3 - 389 + 195 3 - 5 - 233 + 4 + 390 - 5 + 4 6 - 233 + 234 6 - 9 - 233 + 7 + 234 - 9 + 7 + 10 + 234 + + + 10 21 - 246 + 208 21 - 48 - 220 + 42 + 221 - 48 - 127 - 220 + 43 + 95 + 221 - 130 - 946 - 220 + 99 + 491 + 221 - 1416 - 222113 - 142 + 524 + 246988 + 182 @@ -5726,52 +5824,57 @@ 1 2 - 649 + 455 2 3 - 389 + 195 3 - 5 - 233 + 4 + 390 - 5 + 4 6 - 233 + 234 6 - 9 - 233 + 7 + 234 - 9 + 7 + 10 + 234 + + + 10 21 - 246 + 208 21 - 48 - 220 + 42 + 221 - 48 - 127 - 220 + 43 + 95 + 221 - 130 - 946 - 220 + 99 + 491 + 221 - 1416 - 221838 - 142 + 524 + 246621 + 182 @@ -5787,12 +5890,12 @@ 1 2 - 8202149 + 8208085 2 4 - 294143 + 485289 @@ -5808,12 +5911,12 @@ 1 2 - 8202149 + 8208085 2 4 - 294143 + 485289 @@ -5829,12 +5932,12 @@ 1 2 - 8205110 + 8211232 2 3 - 291182 + 482142 @@ -5844,11 +5947,11 @@ ruby_bare_string_child - 14936 + 15074 ruby_bare_string - 11487 + 11622 index @@ -5856,7 +5959,7 @@ child - 14936 + 15074 @@ -5870,12 +5973,12 @@ 1 2 - 11159 + 11291 2 2233 - 328 + 331 @@ -5891,12 +5994,12 @@ 1 2 - 11159 + 11291 2 2233 - 328 + 331 @@ -5926,7 +6029,7 @@ 4 - 11488 + 11623 19 @@ -5957,7 +6060,7 @@ 4 - 11488 + 11623 19 @@ -5974,7 +6077,7 @@ 1 2 - 14936 + 15074 @@ -5990,7 +6093,7 @@ 1 2 - 14936 + 15074 @@ -6000,22 +6103,22 @@ ruby_bare_string_def - 11487 + 11622 id - 11487 + 11622 ruby_bare_symbol_child - 2106 + 2085 ruby_bare_symbol - 2106 + 2085 index @@ -6023,7 +6126,7 @@ child - 2106 + 2085 @@ -6037,7 +6140,7 @@ 1 2 - 2106 + 2085 @@ -6053,7 +6156,7 @@ 1 2 - 2106 + 2085 @@ -6067,8 +6170,8 @@ 12 - 709 - 710 + 715 + 716 2 @@ -6083,8 +6186,8 @@ 12 - 709 - 710 + 715 + 716 2 @@ -6101,7 +6204,7 @@ 1 2 - 2106 + 2085 @@ -6117,7 +6220,7 @@ 1 2 - 2106 + 2085 @@ -6127,11 +6230,11 @@ ruby_bare_symbol_def - 2106 + 2085 id - 2106 + 2085 @@ -6365,11 +6468,11 @@ ruby_begin_child - 7535 + 7615 ruby_begin - 2523 + 2549 index @@ -6377,7 +6480,7 @@ child - 7535 + 7615 @@ -6391,17 +6494,17 @@ 1 2 - 164 + 160 2 3 - 1317 + 1328 3 4 - 516 + 531 4 @@ -6411,7 +6514,7 @@ 5 8 - 230 + 234 8 @@ -6432,17 +6535,17 @@ 1 2 - 164 + 160 2 3 - 1317 + 1328 3 4 - 516 + 531 4 @@ -6452,7 +6555,7 @@ 5 8 - 230 + 234 8 @@ -6502,11 +6605,11 @@ 24 - 35 + 34 3 - 40 + 39 63 3 @@ -6516,13 +6619,13 @@ 3 - 314 - 1043 + 318 + 1062 3 - 2359 - 2524 + 2389 + 2550 2 @@ -6568,11 +6671,11 @@ 24 - 35 + 34 3 - 40 + 39 63 3 @@ -6582,13 +6685,13 @@ 3 - 314 - 1043 + 318 + 1062 3 - 2359 - 2524 + 2389 + 2550 2 @@ -6605,7 +6708,7 @@ 1 2 - 7535 + 7615 @@ -6621,7 +6724,7 @@ 1 2 - 7535 + 7615 @@ -6631,26 +6734,26 @@ ruby_begin_def - 2523 + 2549 id - 2523 + 2549 ruby_binary_def - 67730 + 68619 id - 67730 + 68619 left - 67730 + 68619 operator @@ -6658,7 +6761,7 @@ right - 67730 + 68619 @@ -6672,7 +6775,7 @@ 1 2 - 67730 + 68619 @@ -6688,7 +6791,7 @@ 1 2 - 67730 + 68619 @@ -6704,7 +6807,7 @@ 1 2 - 67730 + 68619 @@ -6720,7 +6823,7 @@ 1 2 - 67730 + 68619 @@ -6736,7 +6839,7 @@ 1 2 - 67730 + 68619 @@ -6752,7 +6855,7 @@ 1 2 - 67730 + 68619 @@ -6766,68 +6869,68 @@ 12 - 155 - 179 + 157 + 178 2 231 - 369 + 378 2 - 474 - 559 + 491 + 576 2 - 603 - 666 + 622 + 632 2 - 749 - 813 + 758 + 836 2 - 949 - 969 + 956 + 970 2 - 1000 - 1010 + 1011 + 1018 2 - 1248 - 1317 + 1182 + 1258 2 - 1348 - 1633 + 1332 + 1732 2 - 1820 - 1968 + 1804 + 2211 2 - 2381 - 3287 + 2409 + 3329 2 - 6116 - 6739 + 6173 + 6848 2 - 31159 - 31160 + 31539 + 31540 1 @@ -6842,68 +6945,68 @@ 12 - 155 - 179 + 157 + 178 2 231 - 369 + 378 2 - 474 - 559 + 491 + 576 2 - 603 - 666 + 622 + 632 2 - 749 - 813 + 758 + 836 2 - 949 - 969 + 956 + 970 2 - 1000 - 1010 + 1011 + 1018 2 - 1248 - 1317 + 1182 + 1258 2 - 1348 - 1633 + 1332 + 1732 2 - 1820 - 1968 + 1804 + 2211 2 - 2381 - 3287 + 2409 + 3329 2 - 6116 - 6739 + 6173 + 6848 2 - 31159 - 31160 + 31539 + 31540 1 @@ -6918,68 +7021,68 @@ 12 - 155 - 179 + 157 + 178 2 231 - 369 + 378 2 - 474 - 559 + 491 + 576 2 - 603 - 666 + 622 + 632 2 - 749 - 813 + 758 + 836 2 - 949 - 969 + 956 + 970 2 - 1000 - 1010 + 1011 + 1018 2 - 1248 - 1317 + 1182 + 1258 2 - 1348 - 1633 + 1332 + 1732 2 - 1820 - 1968 + 1804 + 2211 2 - 2381 - 3287 + 2409 + 3329 2 - 6116 - 6739 + 6173 + 6848 2 - 31159 - 31160 + 31539 + 31540 1 @@ -6996,7 +7099,7 @@ 1 2 - 67730 + 68619 @@ -7012,7 +7115,7 @@ 1 2 - 67730 + 68619 @@ -7028,7 +7131,7 @@ 1 2 - 67730 + 68619 @@ -7038,15 +7141,15 @@ ruby_block_argument_child - 6050 + 6115 ruby_block_argument - 6050 + 6115 child - 6050 + 6115 @@ -7060,7 +7163,7 @@ 1 2 - 6050 + 6115 @@ -7076,7 +7179,7 @@ 1 2 - 6050 + 6115 @@ -7086,36 +7189,32 @@ ruby_block_argument_def - 6050 + 6115 id - 6050 + 6115 - ruby_block_child - 97203 + ruby_block_body + 97983 ruby_block - 97060 + 97983 - index - 51 - - - child - 97203 + body + 97983 ruby_block - index + body 12 @@ -7123,92 +7222,14 @@ 1 2 - 96969 - - - 2 - 5 - 90 + 97983 - ruby_block - child - - - 12 - - - 1 - 2 - 96969 - - - 2 - 5 - 90 - - - - - - - index - ruby_block - - - 12 - - - 2 - 3 - 25 - - - 7 - 8 - 12 - - - 7474 - 7475 - 12 - - - - - - - index - child - - - 12 - - - 2 - 3 - 25 - - - 7 - 8 - 12 - - - 7474 - 7475 - 12 - - - - - - - child + body ruby_block @@ -7217,23 +7238,7 @@ 1 2 - 97203 - - - - - - - child - index - - - 12 - - - 1 - 2 - 97203 + 97983 @@ -7242,38 +7247,195 @@ - ruby_block_def - 97359 + ruby_block_body_child + 98126 + + + ruby_block_body + 97983 + + + index + 52 + + + child + 98126 + + + + + ruby_block_body + index + + + 12 + + + 1 + 2 + 97892 + + + 2 + 5 + 91 + + + + + + + ruby_block_body + child + + + 12 + + + 1 + 2 + 97892 + + + 2 + 5 + 91 + + + + + + + index + ruby_block_body + + + 12 + + + 2 + 3 + 26 + + + 7 + 8 + 13 + + + 7534 + 7535 + 13 + + + + + + + index + child + + + 12 + + + 2 + 3 + 26 + + + 7 + 8 + 13 + + + 7534 + 7535 + 13 + + + + + + + child + ruby_block_body + + + 12 + + + 1 + 2 + 98126 + + + + + + + child + index + + + 12 + + + 1 + 2 + 98126 + + + + + + + + + ruby_block_body_def + 97983 id - 97359 + 97983 + + + + + + ruby_block_def + 98296 + + + id + 98296 ruby_block_parameter_def - 2410 + 2442 id - 2410 + 2442 ruby_block_parameter_name - 2410 + 2442 ruby_block_parameter - 2410 + 2442 name - 2410 + 2442 @@ -7287,7 +7449,7 @@ 1 2 - 2410 + 2442 @@ -7303,7 +7465,7 @@ 1 2 - 2410 + 2442 @@ -7313,15 +7475,15 @@ ruby_block_parameters - 10459 + 10539 ruby_block - 10459 + 10539 parameters - 10459 + 10539 @@ -7335,7 +7497,7 @@ 1 2 - 10459 + 10539 @@ -7351,7 +7513,7 @@ 1 2 - 10459 + 10539 @@ -7361,11 +7523,11 @@ ruby_block_parameters_child - 27190 + 27656 ruby_block_parameters - 23336 + 23772 index @@ -7373,7 +7535,7 @@ child - 27190 + 27656 @@ -7387,17 +7549,17 @@ 1 2 - 19964 + 20371 2 3 - 3022 + 3053 3 6 - 349 + 347 @@ -7413,17 +7575,17 @@ 1 2 - 19964 + 20371 2 3 - 3022 + 3053 3 6 - 349 + 347 @@ -7437,28 +7599,28 @@ 12 - 9 - 10 + 10 + 11 3 - 33 - 34 + 34 + 35 3 - 111 - 112 + 112 + 113 3 - 1070 - 1071 + 1097 + 1098 3 - 7405 - 7406 + 7669 + 7670 3 @@ -7473,28 +7635,28 @@ 12 - 9 - 10 + 10 + 11 3 - 33 - 34 + 34 + 35 3 - 111 - 112 + 112 + 113 3 - 1070 - 1071 + 1097 + 1098 3 - 7405 - 7406 + 7669 + 7670 3 @@ -7511,7 +7673,7 @@ 1 2 - 27190 + 27656 @@ -7527,7 +7689,7 @@ 1 2 - 27190 + 27656 @@ -7537,11 +7699,11 @@ ruby_block_parameters_def - 23336 + 23772 id - 23336 + 23772 @@ -7683,16 +7845,323 @@ - ruby_break_child - 350 + ruby_body_statement_child + 606175 - ruby_break - 350 + ruby_body_statement + 195703 + + + index + 1103 child - 350 + 606175 + + + + + ruby_body_statement + index + + + 12 + + + 1 + 2 + 89956 + + + 2 + 3 + 35812 + + + 3 + 4 + 23304 + + + 4 + 5 + 14659 + + + 5 + 7 + 15433 + + + 7 + 22 + 14696 + + + 22 + 357 + 1841 + + + + + + + ruby_body_statement + child + + + 12 + + + 1 + 2 + 89956 + + + 2 + 3 + 35812 + + + 3 + 4 + 23304 + + + 4 + 5 + 14659 + + + 5 + 7 + 15433 + + + 7 + 22 + 14696 + + + 22 + 357 + 1841 + + + + + + + index + ruby_body_statement + + + 12 + + + 1 + 2 + 61 + + + 2 + 3 + 102 + + + 3 + 4 + 77 + + + 4 + 5 + 111 + + + 5 + 7 + 83 + + + 7 + 10 + 74 + + + 10 + 13 + 92 + + + 13 + 22 + 86 + + + 22 + 39 + 83 + + + 40 + 66 + 83 + + + 68 + 128 + 83 + + + 130 + 418 + 83 + + + 456 + 63135 + 77 + + + + + + + index + child + + + 12 + + + 1 + 2 + 61 + + + 2 + 3 + 102 + + + 3 + 4 + 77 + + + 4 + 5 + 111 + + + 5 + 7 + 83 + + + 7 + 10 + 74 + + + 10 + 13 + 92 + + + 13 + 22 + 86 + + + 22 + 39 + 83 + + + 40 + 66 + 83 + + + 68 + 128 + 83 + + + 130 + 418 + 83 + + + 456 + 63135 + 77 + + + + + + + child + ruby_body_statement + + + 12 + + + 1 + 2 + 606175 + + + + + + + child + index + + + 12 + + + 1 + 2 + 606175 + + + + + + + + + ruby_body_statement_def + 201560 + + + id + 201560 + + + + + + ruby_break_child + 354 + + + ruby_break + 354 + + + child + 354 @@ -7706,7 +8175,7 @@ 1 2 - 350 + 354 @@ -7722,7 +8191,7 @@ 1 2 - 350 + 354 @@ -7732,26 +8201,26 @@ ruby_break_def - 3338 + 3355 id - 3338 + 3355 ruby_call_arguments - 661504 + 667146 ruby_call - 661504 + 667146 arguments - 661504 + 667146 @@ -7765,7 +8234,7 @@ 1 2 - 661504 + 667146 @@ -7781,7 +8250,7 @@ 1 2 - 661504 + 667146 @@ -7791,15 +8260,15 @@ ruby_call_block - 232457 + 233892 ruby_call - 232457 + 233892 block - 232457 + 233892 @@ -7813,7 +8282,7 @@ 1 2 - 232457 + 233892 @@ -7829,7 +8298,7 @@ 1 2 - 232457 + 233892 @@ -7839,26 +8308,26 @@ ruby_call_def - 965401 + 974897 id - 965401 + 974897 ruby_call_method - 965401 + 974897 ruby_call - 965401 + 974897 method - 965401 + 974897 @@ -7872,7 +8341,7 @@ 1 2 - 965401 + 974897 @@ -7888,7 +8357,7 @@ 1 2 - 965401 + 974897 @@ -7898,15 +8367,15 @@ ruby_call_operator - 540944 + 548273 ruby_call - 540944 + 548273 operator - 540944 + 548273 @@ -7920,7 +8389,7 @@ 1 2 - 540944 + 548273 @@ -7936,7 +8405,7 @@ 1 2 - 540944 + 548273 @@ -7946,15 +8415,15 @@ ruby_call_receiver - 540944 + 548273 ruby_call - 540944 + 548273 receiver - 540944 + 548273 @@ -7968,7 +8437,7 @@ 1 2 - 540944 + 548273 @@ -7984,7 +8453,7 @@ 1 2 - 540944 + 548273 @@ -7994,19 +8463,19 @@ ruby_case_child - 4141 + 4153 ruby_case__ - 1219 + 1224 index - 69 + 68 child - 4141 + 4153 @@ -8025,12 +8494,12 @@ 2 3 - 315 + 316 3 4 - 504 + 511 4 @@ -8040,12 +8509,12 @@ 5 7 - 107 + 105 7 23 - 66 + 65 @@ -8066,12 +8535,12 @@ 2 3 - 315 + 316 3 4 - 504 + 511 4 @@ -8081,12 +8550,12 @@ 5 7 - 107 + 105 7 23 - 66 + 65 @@ -8102,12 +8571,12 @@ 1 2 - 25 + 21 2 3 - 6 + 9 3 @@ -8125,18 +8594,18 @@ 6 - 32 + 34 56 6 - 115 - 276 + 116 + 282 6 - 375 - 388 + 383 + 396 6 @@ -8153,12 +8622,12 @@ 1 2 - 25 + 21 2 3 - 6 + 9 3 @@ -8176,18 +8645,18 @@ 6 - 32 + 34 56 6 - 115 - 276 + 116 + 282 6 - 375 - 388 + 383 + 396 6 @@ -8204,7 +8673,7 @@ 1 2 - 4141 + 4153 @@ -8220,7 +8689,7 @@ 1 2 - 4141 + 4153 @@ -8230,11 +8699,11 @@ ruby_case_def - 1219 + 1224 id - 1219 + 1224 @@ -8423,15 +8892,15 @@ ruby_case_value - 1178 + 1184 ruby_case__ - 1178 + 1184 value - 1178 + 1184 @@ -8445,7 +8914,7 @@ 1 2 - 1178 + 1184 @@ -8461,7 +8930,7 @@ 1 2 - 1178 + 1184 @@ -8471,11 +8940,11 @@ ruby_chained_string_child - 3353 + 3329 ruby_chained_string - 885 + 877 index @@ -8483,7 +8952,7 @@ child - 3353 + 3329 @@ -8497,32 +8966,32 @@ 2 3 - 302 + 291 3 4 - 195 + 201 4 5 - 135 + 133 5 6 - 122 + 123 6 8 - 66 + 65 8 13 - 63 + 61 @@ -8538,32 +9007,32 @@ 2 3 - 302 + 291 3 4 - 195 + 201 4 5 - 135 + 133 5 6 - 122 + 123 6 8 - 66 + 65 8 13 - 63 + 61 @@ -8612,23 +9081,23 @@ 3 - 80 - 81 + 81 + 82 3 - 123 - 124 + 124 + 125 3 - 185 - 186 + 189 + 190 3 - 281 - 282 + 283 + 284 6 @@ -8678,23 +9147,23 @@ 3 - 80 - 81 + 81 + 82 3 - 123 - 124 + 124 + 125 3 - 185 - 186 + 189 + 190 3 - 281 - 282 + 283 + 284 6 @@ -8711,7 +9180,7 @@ 1 2 - 3353 + 3329 @@ -8727,7 +9196,7 @@ 1 2 - 3353 + 3329 @@ -8737,36 +9206,32 @@ ruby_chained_string_def - 885 + 877 id - 885 + 877 - ruby_class_child - 130958 + ruby_class_body + 15291 ruby_class - 15183 + 15291 - index - 1055 - - - child - 130958 + body + 15291 ruby_class - index + body 12 @@ -8774,130 +9239,14 @@ 1 2 - 3290 - - - 2 - 3 - 2407 - - - 3 - 4 - 1556 - - - 4 - 5 - 1263 - - - 5 - 6 - 961 - - - 6 - 7 - 838 - - - 7 - 9 - 1118 - - - 9 - 13 - 1266 - - - 13 - 21 - 1169 - - - 21 - 84 - 1143 - - - 85 - 336 - 167 + 15291 - ruby_class - child - - - 12 - - - 1 - 2 - 3290 - - - 2 - 3 - 2407 - - - 3 - 4 - 1556 - - - 4 - 5 - 1263 - - - 5 - 6 - 961 - - - 6 - 7 - 838 - - - 7 - 9 - 1118 - - - 9 - 13 - 1266 - - - 13 - 21 - 1169 - - - 21 - 84 - 1143 - - - 85 - 336 - 167 - - - - - - - index + body ruby_class @@ -8906,185 +9255,7 @@ 1 2 - 37 - - - 2 - 3 - 63 - - - 3 - 4 - 81 - - - 4 - 5 - 113 - - - 5 - 7 - 81 - - - 7 - 9 - 81 - - - 9 - 12 - 88 - - - 12 - 19 - 85 - - - 19 - 29 - 81 - - - 30 - 56 - 81 - - - 56 - 94 - 81 - - - 96 - 228 - 81 - - - 237 - 2116 - 81 - - - 2516 - 4819 - 12 - - - - - - - index - child - - - 12 - - - 1 - 2 - 37 - - - 2 - 3 - 63 - - - 3 - 4 - 81 - - - 4 - 5 - 113 - - - 5 - 7 - 81 - - - 7 - 9 - 81 - - - 9 - 12 - 88 - - - 12 - 19 - 85 - - - 19 - 29 - 81 - - - 30 - 56 - 81 - - - 56 - 94 - 81 - - - 96 - 228 - 81 - - - 237 - 2116 - 81 - - - 2516 - 4819 - 12 - - - - - - - child - ruby_class - - - 12 - - - 1 - 2 - 130958 - - - - - - - child - index - - - 12 - - - 1 - 2 - 130958 + 15291 @@ -9094,15 +9265,15 @@ ruby_class_def - 16841 + 16946 id - 16841 + 16946 name - 16841 + 16946 @@ -9116,7 +9287,7 @@ 1 2 - 16841 + 16946 @@ -9132,7 +9303,7 @@ 1 2 - 16841 + 16946 @@ -9142,15 +9313,15 @@ ruby_class_superclass - 13314 + 13378 ruby_class - 13314 + 13378 superclass - 13314 + 13378 @@ -9164,7 +9335,7 @@ 1 2 - 13314 + 13378 @@ -9180,7 +9351,7 @@ 1 2 - 13314 + 13378 @@ -9238,23 +9409,23 @@ ruby_conditional_def - 3464 + 3495 id - 3464 + 3495 alternative - 3464 + 3495 condition - 3464 + 3495 consequence - 3464 + 3495 @@ -9268,7 +9439,7 @@ 1 2 - 3464 + 3495 @@ -9284,7 +9455,7 @@ 1 2 - 3464 + 3495 @@ -9300,7 +9471,7 @@ 1 2 - 3464 + 3495 @@ -9316,7 +9487,7 @@ 1 2 - 3464 + 3495 @@ -9332,7 +9503,7 @@ 1 2 - 3464 + 3495 @@ -9348,7 +9519,7 @@ 1 2 - 3464 + 3495 @@ -9364,7 +9535,7 @@ 1 2 - 3464 + 3495 @@ -9380,7 +9551,7 @@ 1 2 - 3464 + 3495 @@ -9396,7 +9567,7 @@ 1 2 - 3464 + 3495 @@ -9412,7 +9583,7 @@ 1 2 - 3464 + 3495 @@ -9428,7 +9599,7 @@ 1 2 - 3464 + 3495 @@ -9444,7 +9615,7 @@ 1 2 - 3464 + 3495 @@ -9454,19 +9625,19 @@ ruby_delimited_symbol_child - 1739 + 1757 ruby_delimited_symbol - 1257 + 1264 index - 25 + 24 child - 1739 + 1757 @@ -9480,17 +9651,17 @@ 1 2 - 939 + 936 2 3 - 245 + 254 3 9 - 72 + 74 @@ -9506,17 +9677,17 @@ 1 2 - 939 + 936 2 3 - 245 + 254 3 9 - 72 + 74 @@ -9555,18 +9726,18 @@ 3 - 23 - 24 + 24 + 25 3 - 101 - 102 + 106 + 107 3 - 399 - 400 + 408 + 409 3 @@ -9606,18 +9777,18 @@ 3 - 23 - 24 + 24 + 25 3 - 101 - 102 + 106 + 107 3 - 399 - 400 + 408 + 409 3 @@ -9634,7 +9805,7 @@ 1 2 - 1739 + 1757 @@ -9650,7 +9821,7 @@ 1 2 - 1739 + 1757 @@ -9660,11 +9831,11 @@ ruby_delimited_symbol_def - 1257 + 1264 id - 1257 + 1264 @@ -9858,19 +10029,19 @@ ruby_destructured_parameter_child - 406 + 412 ruby_destructured_parameter - 198 + 188 index - 12 + 11 child - 406 + 412 @@ -9884,17 +10055,22 @@ 1 2 - 9 + 15 2 3 - 173 + 146 3 - 5 - 15 + 4 + 18 + + + 4 + 12 + 9 @@ -9910,17 +10086,22 @@ 1 2 - 9 + 15 2 3 - 173 + 146 3 - 5 - 15 + 4 + 18 + + + 4 + 12 + 9 @@ -9936,22 +10117,37 @@ 1 2 - 3 + 1 - 5 - 6 - 3 + 2 + 3 + 5 - 60 - 61 - 3 + 4 + 5 + 1 - 63 - 64 - 3 + 9 + 10 + 1 + + + 27 + 28 + 1 + + + 173 + 174 + 1 + + + 188 + 189 + 1 @@ -9967,22 +10163,37 @@ 1 2 - 3 + 1 - 5 - 6 - 3 + 2 + 3 + 5 - 60 - 61 - 3 + 4 + 5 + 1 - 63 - 64 - 3 + 9 + 10 + 1 + + + 27 + 28 + 1 + + + 173 + 174 + 1 + + + 188 + 189 + 1 @@ -9998,7 +10209,7 @@ 1 2 - 406 + 412 @@ -10014,7 +10225,7 @@ 1 2 - 406 + 412 @@ -10034,26 +10245,22 @@ - ruby_do_block_child - 395009 + ruby_do_block_body + 137963 ruby_do_block - 137279 + 137963 - index - 935 - - - child - 395009 + body + 137963 ruby_do_block - index + body 12 @@ -10061,80 +10268,14 @@ 1 2 - 48036 - - - 2 - 3 - 36712 - - - 3 - 4 - 21583 - - - 4 - 5 - 10506 - - - 5 - 7 - 10583 - - - 7 - 73 - 9856 + 137963 - ruby_do_block - child - - - 12 - - - 1 - 2 - 48036 - - - 2 - 3 - 36712 - - - 3 - 4 - 21583 - - - 4 - 5 - 10506 - - - 5 - 7 - 10583 - - - 7 - 73 - 9856 - - - - - - - index + body ruby_do_block @@ -10143,165 +10284,7 @@ 1 2 - 51 - - - 2 - 3 - 220 - - - 4 - 7 - 77 - - - 7 - 8 - 51 - - - 8 - 11 - 77 - - - 11 - 14 - 51 - - - 14 - 18 - 77 - - - 21 - 54 - 77 - - - 62 - 142 - 77 - - - 175 - 592 - 77 - - - 759 - 6873 - 77 - - - 10571 - 10572 - 12 - - - - - - - index - child - - - 12 - - - 1 - 2 - 51 - - - 2 - 3 - 220 - - - 4 - 7 - 77 - - - 7 - 8 - 51 - - - 8 - 11 - 77 - - - 11 - 14 - 51 - - - 14 - 18 - 77 - - - 21 - 54 - 77 - - - 62 - 142 - 77 - - - 175 - 592 - 77 - - - 759 - 6873 - 77 - - - 10571 - 10572 - 12 - - - - - - - child - ruby_do_block - - - 12 - - - 1 - 2 - 395009 - - - - - - - child - index - - - 12 - - - 1 - 2 - 395009 + 137963 @@ -10311,26 +10294,26 @@ ruby_do_block_def - 137435 + 138119 id - 137435 + 138119 ruby_do_block_parameters - 15073 + 15269 ruby_do_block - 15073 + 15269 parameters - 15073 + 15269 @@ -10344,7 +10327,7 @@ 1 2 - 15073 + 15269 @@ -10360,7 +10343,7 @@ 1 2 - 15073 + 15269 @@ -10370,11 +10353,11 @@ ruby_do_child - 9177 + 9244 ruby_do - 1588 + 1608 index @@ -10382,7 +10365,7 @@ child - 9177 + 9244 @@ -10396,27 +10379,27 @@ 1 2 - 324 + 331 2 3 - 273 + 279 3 4 - 191 + 194 4 5 - 77 + 78 5 7 - 104 + 106 7 @@ -10431,12 +10414,12 @@ 9 14 - 117 + 115 14 18 - 121 + 124 18 @@ -10457,27 +10440,27 @@ 1 2 - 324 + 331 2 3 - 273 + 279 3 4 - 191 + 194 4 5 - 77 + 78 5 7 - 104 + 106 7 @@ -10492,12 +10475,12 @@ 9 14 - 117 + 115 14 18 - 121 + 124 18 @@ -10537,12 +10520,12 @@ 6 - 59 + 61 16 - 112 - 1589 + 114 + 1609 15 @@ -10578,12 +10561,12 @@ 6 - 59 + 61 16 - 112 - 1589 + 114 + 1609 15 @@ -10600,7 +10583,7 @@ 1 2 - 9177 + 9244 @@ -10616,7 +10599,7 @@ 1 2 - 9177 + 9244 @@ -10626,22 +10609,22 @@ ruby_do_def - 1610 + 1632 id - 1610 + 1632 ruby_element_reference_child - 82398 + 83029 ruby_element_reference - 82228 + 82863 index @@ -10649,7 +10632,7 @@ child - 82398 + 83029 @@ -10663,12 +10646,12 @@ 1 2 - 82059 + 82697 2 3 - 169 + 166 @@ -10684,12 +10667,12 @@ 1 2 - 82059 + 82697 2 3 - 169 + 166 @@ -10708,8 +10691,8 @@ 2 - 27674 - 27675 + 28403 + 28404 2 @@ -10729,8 +10712,8 @@ 2 - 27674 - 27675 + 28403 + 28404 2 @@ -10747,7 +10730,7 @@ 1 2 - 82398 + 83029 @@ -10763,7 +10746,7 @@ 1 2 - 82398 + 83029 @@ -10773,15 +10756,15 @@ ruby_element_reference_def - 82234 + 82869 id - 82234 + 82869 object - 82234 + 82869 @@ -10795,7 +10778,7 @@ 1 2 - 82234 + 82869 @@ -10811,7 +10794,7 @@ 1 2 - 82234 + 82869 @@ -10821,11 +10804,11 @@ ruby_else_child - 8802 + 9054 ruby_else - 6923 + 7126 index @@ -10833,7 +10816,7 @@ child - 8802 + 9054 @@ -10847,17 +10830,17 @@ 1 2 - 5833 + 6004 2 3 - 687 + 703 3 12 - 403 + 418 @@ -10873,17 +10856,17 @@ 1 2 - 5833 + 6004 2 3 - 687 + 703 3 12 - 403 + 418 @@ -10927,28 +10910,28 @@ 3 - 29 - 30 + 28 + 29 3 - 55 - 56 + 59 + 60 3 - 128 - 129 + 135 + 136 3 - 346 - 347 + 362 + 363 3 - 2197 - 2198 + 2299 + 2300 3 @@ -10993,28 +10976,28 @@ 3 - 29 - 30 + 28 + 29 3 - 55 - 56 + 59 + 60 3 - 128 - 129 + 135 + 136 3 - 346 - 347 + 362 + 363 3 - 2197 - 2198 + 2299 + 2300 3 @@ -11031,7 +11014,7 @@ 1 2 - 8802 + 9054 @@ -11047,7 +11030,7 @@ 1 2 - 8802 + 9054 @@ -11057,26 +11040,26 @@ ruby_else_def - 6933 + 7138 id - 6933 + 7138 ruby_elsif_alternative - 903 + 936 ruby_elsif - 903 + 936 alternative - 903 + 936 @@ -11090,7 +11073,7 @@ 1 2 - 903 + 936 @@ -11106,7 +11089,7 @@ 1 2 - 903 + 936 @@ -11116,15 +11099,15 @@ ruby_elsif_consequence - 1589 + 1572 ruby_elsif - 1589 + 1572 consequence - 1589 + 1572 @@ -11138,7 +11121,7 @@ 1 2 - 1589 + 1572 @@ -11154,7 +11137,7 @@ 1 2 - 1589 + 1572 @@ -11164,15 +11147,15 @@ ruby_elsif_def - 1595 + 1578 id - 1595 + 1578 condition - 1595 + 1578 @@ -11186,7 +11169,7 @@ 1 2 - 1595 + 1578 @@ -11202,7 +11185,7 @@ 1 2 - 1595 + 1578 @@ -11399,19 +11382,19 @@ ruby_ensure_child - 4834 + 4925 ruby_ensure - 3718 + 3803 index - 50 + 49 child - 4834 + 4925 @@ -11425,17 +11408,17 @@ 1 2 - 2956 + 3034 2 3 - 516 + 526 3 17 - 245 + 241 @@ -11451,17 +11434,17 @@ 1 2 - 2956 + 3034 2 3 - 516 + 526 3 17 - 245 + 241 @@ -11477,7 +11460,7 @@ 1 2 - 25 + 24 3 @@ -11490,8 +11473,8 @@ 6 - 12 - 13 + 14 + 15 3 @@ -11500,13 +11483,13 @@ 3 - 242 - 243 + 248 + 249 3 - 1180 - 1181 + 1227 + 1228 3 @@ -11523,7 +11506,7 @@ 1 2 - 25 + 24 3 @@ -11536,8 +11519,8 @@ 6 - 12 - 13 + 14 + 15 3 @@ -11546,13 +11529,13 @@ 3 - 242 - 243 + 248 + 249 3 - 1180 - 1181 + 1227 + 1228 3 @@ -11569,7 +11552,7 @@ 1 2 - 4834 + 4925 @@ -11585,7 +11568,7 @@ 1 2 - 4834 + 4925 @@ -11595,26 +11578,26 @@ ruby_ensure_def - 3718 + 3803 id - 3718 + 3803 ruby_exception_variable_def - 1001 + 991 id - 1001 + 991 child - 1001 + 991 @@ -11628,7 +11611,7 @@ 1 2 - 1001 + 991 @@ -11644,7 +11627,7 @@ 1 2 - 1001 + 991 @@ -11654,11 +11637,11 @@ ruby_exceptions_child - 1849 + 1873 ruby_exceptions - 1639 + 1654 index @@ -11666,7 +11649,7 @@ child - 1849 + 1873 @@ -11680,17 +11663,17 @@ 1 2 - 1497 + 1509 2 4 - 129 + 131 4 10 - 13 + 14 @@ -11706,17 +11689,17 @@ 1 2 - 1497 + 1509 2 4 - 129 + 131 4 10 - 13 + 14 @@ -11745,28 +11728,28 @@ 1 - 8 - 9 + 9 + 10 1 - 13 - 14 + 14 + 15 1 - 38 - 39 + 42 + 43 1 - 142 - 143 + 145 + 146 1 - 1639 - 1640 + 1654 + 1655 1 @@ -11796,28 +11779,28 @@ 1 - 8 - 9 + 9 + 10 1 - 13 - 14 + 14 + 15 1 - 38 - 39 + 42 + 43 1 - 142 - 143 + 145 + 146 1 - 1639 - 1640 + 1654 + 1655 1 @@ -11834,7 +11817,7 @@ 1 2 - 1849 + 1873 @@ -11850,7 +11833,7 @@ 1 2 - 1849 + 1873 @@ -11860,11 +11843,11 @@ ruby_exceptions_def - 1639 + 1654 id - 1639 + 1654 @@ -12280,19 +12263,19 @@ ruby_hash_child - 88983 + 89413 ruby_hash - 35712 + 35882 index - 1389 + 1391 child - 88983 + 89413 @@ -12306,32 +12289,32 @@ 1 2 - 14778 + 14813 2 3 - 9960 + 10040 3 4 - 3960 + 3992 4 5 - 4142 + 4161 5 19 - 2688 + 2692 19 108 - 181 + 182 @@ -12347,32 +12330,32 @@ 1 2 - 14778 + 14813 2 3 - 9960 + 10040 3 4 - 3960 + 3992 4 5 - 4142 + 4161 5 19 - 2688 + 2692 19 108 - 181 + 182 @@ -12388,32 +12371,32 @@ 1 2 - 922 + 923 3 4 - 103 + 104 5 11 - 116 + 117 14 51 - 116 + 117 57 - 1613 - 116 + 1621 + 117 - 2750 - 2751 - 12 + 2759 + 2760 + 13 @@ -12429,32 +12412,32 @@ 1 2 - 922 + 923 3 4 - 103 + 104 5 11 - 116 + 117 14 51 - 116 + 117 57 - 1613 - 116 + 1621 + 117 - 2750 - 2751 - 12 + 2759 + 2760 + 13 @@ -12470,7 +12453,7 @@ 1 2 - 88983 + 89413 @@ -12486,7 +12469,7 @@ 1 2 - 88983 + 89413 @@ -12496,11 +12479,11 @@ ruby_hash_def - 39413 + 39640 id - 39413 + 39640 @@ -12658,15 +12641,15 @@ ruby_hash_splat_argument_def - 1856 + 1857 id - 1856 + 1857 child - 1856 + 1857 @@ -12680,7 +12663,7 @@ 1 2 - 1856 + 1857 @@ -12696,7 +12679,7 @@ 1 2 - 1856 + 1857 @@ -12706,26 +12689,26 @@ ruby_hash_splat_parameter_def - 1355 + 1447 id - 1355 + 1447 ruby_hash_splat_parameter_name - 1134 + 1218 ruby_hash_splat_parameter - 1134 + 1218 name - 1134 + 1218 @@ -12739,7 +12722,7 @@ 1 2 - 1134 + 1218 @@ -12755,7 +12738,7 @@ 1 2 - 1134 + 1218 @@ -12765,19 +12748,19 @@ ruby_heredoc_body_child - 25318 + 25699 ruby_heredoc_body - 5363 + 5519 index - 264 + 259 child - 25318 + 25699 @@ -12791,12 +12774,12 @@ 2 3 - 2929 + 3080 4 5 - 692 + 685 5 @@ -12806,22 +12789,22 @@ 6 7 - 781 + 787 7 9 - 341 + 335 10 15 - 421 + 440 16 90 - 193 + 186 @@ -12837,12 +12820,12 @@ 2 3 - 2929 + 3080 4 5 - 692 + 685 5 @@ -12852,22 +12835,22 @@ 6 7 - 781 + 787 7 9 - 341 + 335 10 15 - 421 + 440 16 90 - 193 + 186 @@ -12883,12 +12866,17 @@ 1 2 - 50 + 49 2 3 - 83 + 46 + + + 3 + 4 + 35 4 @@ -12897,11 +12885,11 @@ 8 - 12 + 13 23 - 12 + 13 26 23 @@ -12912,12 +12900,12 @@ 96 - 323 + 331 20 - 585 - 1806 + 600 + 1893 17 @@ -12934,12 +12922,17 @@ 1 2 - 50 + 49 2 3 - 83 + 46 + + + 3 + 4 + 35 4 @@ -12948,11 +12941,11 @@ 8 - 12 + 13 23 - 12 + 13 26 23 @@ -12963,12 +12956,12 @@ 96 - 323 + 331 20 - 585 - 1806 + 600 + 1893 17 @@ -12985,7 +12978,7 @@ 1 2 - 25318 + 25699 @@ -13001,7 +12994,7 @@ 1 2 - 25318 + 25699 @@ -13011,26 +13004,26 @@ ruby_heredoc_body_def - 5575 + 5789 id - 5575 + 5789 ruby_if_alternative - 6454 + 6667 ruby_if - 6454 + 6667 alternative - 6454 + 6667 @@ -13044,7 +13037,7 @@ 1 2 - 6454 + 6667 @@ -13060,7 +13053,7 @@ 1 2 - 6454 + 6667 @@ -13070,15 +13063,15 @@ ruby_if_consequence - 18324 + 18280 ruby_if - 18324 + 18280 consequence - 18324 + 18280 @@ -13092,7 +13085,7 @@ 1 2 - 18324 + 18280 @@ -13108,7 +13101,7 @@ 1 2 - 18324 + 18280 @@ -13118,15 +13111,15 @@ ruby_if_def - 18392 + 18341 id - 18392 + 18341 condition - 18392 + 18341 @@ -13140,7 +13133,7 @@ 1 2 - 18392 + 18341 @@ -13156,7 +13149,7 @@ 1 2 - 18392 + 18341 @@ -13208,19 +13201,19 @@ ruby_if_modifier_def - 13620 + 13708 id - 13620 + 13708 body - 13620 + 13708 condition - 13620 + 13708 @@ -13234,7 +13227,7 @@ 1 2 - 13620 + 13708 @@ -13250,7 +13243,7 @@ 1 2 - 13620 + 13708 @@ -13266,7 +13259,7 @@ 1 2 - 13620 + 13708 @@ -13282,7 +13275,7 @@ 1 2 - 13620 + 13708 @@ -13298,7 +13291,7 @@ 1 2 - 13620 + 13708 @@ -13314,7 +13307,7 @@ 1 2 - 13620 + 13708 @@ -13510,11 +13503,11 @@ ruby_interpolation_child - 38208 + 38051 ruby_interpolation - 38208 + 38051 index @@ -13522,7 +13515,7 @@ child - 38208 + 38051 @@ -13536,7 +13529,7 @@ 1 2 - 38208 + 38051 @@ -13552,7 +13545,7 @@ 1 2 - 38208 + 38051 @@ -13566,8 +13559,8 @@ 12 - 12859 - 12860 + 13043 + 13044 2 @@ -13582,8 +13575,8 @@ 12 - 12859 - 12860 + 13043 + 13044 2 @@ -13600,7 +13593,7 @@ 1 2 - 38208 + 38051 @@ -13616,7 +13609,7 @@ 1 2 - 38208 + 38051 @@ -13626,26 +13619,26 @@ ruby_interpolation_def - 38208 + 38051 id - 38208 + 38051 ruby_keyword_parameter_def - 3794 + 3939 id - 3794 + 3939 name - 3794 + 3939 @@ -13659,7 +13652,7 @@ 1 2 - 3794 + 3939 @@ -13675,7 +13668,7 @@ 1 2 - 3794 + 3939 @@ -13685,15 +13678,15 @@ ruby_keyword_parameter_value - 2874 + 2985 ruby_keyword_parameter - 2874 + 2985 value - 2874 + 2985 @@ -13707,7 +13700,7 @@ 1 2 - 2874 + 2985 @@ -13723,7 +13716,7 @@ 1 2 - 2874 + 2985 @@ -13823,15 +13816,15 @@ ruby_lambda_def - 7472 + 7557 id - 7472 + 7557 body - 7472 + 7557 @@ -13845,7 +13838,7 @@ 1 2 - 7472 + 7557 @@ -13861,7 +13854,7 @@ 1 2 - 7472 + 7557 @@ -13871,15 +13864,15 @@ ruby_lambda_parameters - 1664 + 1665 ruby_lambda - 1664 + 1665 parameters - 1664 + 1665 @@ -13893,7 +13886,7 @@ 1 2 - 1664 + 1665 @@ -13909,7 +13902,7 @@ 1 2 - 1664 + 1665 @@ -13919,11 +13912,11 @@ ruby_lambda_parameters_child - 1905 + 1907 ruby_lambda_parameters - 1655 + 1656 index @@ -13931,7 +13924,7 @@ child - 1905 + 1907 @@ -13950,7 +13943,7 @@ 2 3 - 144 + 145 3 @@ -13976,7 +13969,7 @@ 2 3 - 144 + 145 3 @@ -14020,13 +14013,13 @@ 1 - 183 - 184 + 184 + 185 1 - 1655 - 1656 + 1656 + 1657 1 @@ -14066,13 +14059,13 @@ 1 - 183 - 184 + 184 + 185 1 - 1655 - 1656 + 1656 + 1657 1 @@ -14089,7 +14082,7 @@ 1 2 - 1905 + 1907 @@ -14105,7 +14098,7 @@ 1 2 - 1905 + 1907 @@ -14115,22 +14108,22 @@ ruby_lambda_parameters_def - 1664 + 1665 id - 1664 + 1665 ruby_left_assignment_list_child - 6358 + 6436 ruby_left_assignment_list - 2873 + 2910 index @@ -14138,7 +14131,7 @@ child - 6358 + 6436 @@ -14152,17 +14145,17 @@ 1 2 - 360 + 361 2 3 - 1861 + 1892 3 4 - 489 + 494 4 @@ -14183,17 +14176,17 @@ 1 2 - 360 + 361 2 3 - 1861 + 1892 3 4 - 489 + 494 4 @@ -14257,18 +14250,18 @@ 1 - 652 - 653 + 657 + 658 1 - 2513 - 2514 + 2549 + 2550 1 - 2873 - 2874 + 2910 + 2911 1 @@ -14328,18 +14321,18 @@ 1 - 652 - 653 + 657 + 658 1 - 2513 - 2514 + 2549 + 2550 1 - 2873 - 2874 + 2910 + 2911 1 @@ -14356,7 +14349,7 @@ 1 2 - 6358 + 6436 @@ -14372,7 +14365,7 @@ 1 2 - 6358 + 6436 @@ -14382,36 +14375,32 @@ ruby_left_assignment_list_def - 2873 + 2910 id - 2873 + 2910 - ruby_method_child - 264218 + ruby_method_body + 98487 ruby_method - 97351 + 98487 - index - 239 - - - child - 264218 + body + 98487 ruby_method - index + body 12 @@ -14419,80 +14408,14 @@ 1 2 - 43682 - - - 2 - 3 - 18007 - - - 3 - 4 - 12772 - - - 4 - 5 - 7790 - - - 5 - 7 - 8159 - - - 7 - 77 - 6939 + 98487 - ruby_method - child - - - 12 - - - 1 - 2 - 43682 - - - 2 - 3 - 18007 - - - 3 - 4 - 12772 - - - 4 - 5 - 7790 - - - 5 - 7 - 8159 - - - 7 - 77 - 6939 - - - - - - - index + body ruby_method @@ -14501,165 +14424,7 @@ 1 2 - 22 - - - 2 - 4 - 6 - - - 4 - 5 - 28 - - - 5 - 6 - 31 - - - 6 - 7 - 18 - - - 7 - 13 - 18 - - - 14 - 20 - 18 - - - 21 - 41 - 18 - - - 49 - 115 - 18 - - - 150 - 400 - 18 - - - 511 - 2203 - 18 - - - 3217 - 30892 - 18 - - - - - - - index - child - - - 12 - - - 1 - 2 - 22 - - - 2 - 4 - 6 - - - 4 - 5 - 28 - - - 5 - 6 - 31 - - - 6 - 7 - 18 - - - 7 - 13 - 18 - - - 14 - 20 - 18 - - - 21 - 41 - 18 - - - 49 - 115 - 18 - - - 150 - 400 - 18 - - - 511 - 2203 - 18 - - - 3217 - 30892 - 18 - - - - - - - child - ruby_method - - - 12 - - - 1 - 2 - 264218 - - - - - - - child - index - - - 12 - - - 1 - 2 - 264218 + 98487 @@ -14669,15 +14434,15 @@ ruby_method_def - 98356 + 99525 id - 98356 + 99525 name - 98356 + 99525 @@ -14691,7 +14456,7 @@ 1 2 - 98356 + 99525 @@ -14707,7 +14472,7 @@ 1 2 - 98356 + 99525 @@ -14717,15 +14482,15 @@ ruby_method_parameters - 27238 + 27765 ruby_method - 27238 + 27765 parameters - 27238 + 27765 @@ -14739,7 +14504,7 @@ 1 2 - 27238 + 27765 @@ -14755,7 +14520,7 @@ 1 2 - 27238 + 27765 @@ -14765,19 +14530,19 @@ ruby_method_parameters_child - 47287 + 48416 ruby_method_parameters - 28643 + 29203 index - 34 + 37 child - 47287 + 48416 @@ -14791,22 +14556,22 @@ 1 2 - 17310 + 17656 2 3 - 7021 + 7064 3 4 - 2678 + 2765 4 - 12 - 1632 + 13 + 1717 @@ -14822,22 +14587,22 @@ 1 2 - 17310 + 17656 2 3 - 7021 + 7064 3 4 - 2678 + 2765 4 - 12 - 1632 + 13 + 1717 @@ -14850,6 +14615,11 @@ 12 + + 1 + 2 + 3 + 3 4 @@ -14861,48 +14631,48 @@ 3 - 9 - 10 + 11 + 12 3 - 28 - 29 + 29 + 30 3 - 48 - 49 + 53 + 54 3 - 115 - 116 + 125 + 126 3 - 226 - 227 + 246 + 247 3 - 518 - 519 + 554 + 555 3 - 1368 - 1369 + 1446 + 1447 3 - 3596 - 3597 + 3725 + 3726 3 - 9089 - 9090 + 9421 + 9422 3 @@ -14916,6 +14686,11 @@ 12 + + 1 + 2 + 3 + 3 4 @@ -14927,48 +14702,48 @@ 3 - 9 - 10 + 11 + 12 3 - 28 - 29 + 29 + 30 3 - 48 - 49 + 53 + 54 3 - 115 - 116 + 125 + 126 3 - 226 - 227 + 246 + 247 3 - 518 - 519 + 554 + 555 3 - 1368 - 1369 + 1446 + 1447 3 - 3596 - 3597 + 3725 + 3726 3 - 9089 - 9090 + 9421 + 9422 3 @@ -14985,7 +14760,7 @@ 1 2 - 47287 + 48416 @@ -15001,7 +14776,7 @@ 1 2 - 47287 + 48416 @@ -15011,36 +14786,32 @@ ruby_method_parameters_def - 28861 + 29417 id - 28861 + 29417 - ruby_module_child - 31303 + ruby_module_body + 21511 ruby_module - 10513 + 21511 - index - 393 - - - child - 31303 + body + 21511 ruby_module - index + body 12 @@ -15048,70 +14819,14 @@ 1 2 - 7443 - - - 2 - 3 - 863 - - - 3 - 5 - 772 - - - 5 - 10 - 794 - - - 10 - 126 - 639 + 21511 - ruby_module - child - - - 12 - - - 1 - 2 - 7443 - - - 2 - 3 - 863 - - - 3 - 5 - 772 - - - 5 - 10 - 794 - - - 10 - 126 - 639 - - - - - - - index + body ruby_module @@ -15120,165 +14835,7 @@ 1 2 - 31 - - - 2 - 3 - 37 - - - 3 - 4 - 9 - - - 4 - 5 - 72 - - - 5 - 8 - 31 - - - 8 - 10 - 31 - - - 10 - 17 - 34 - - - 17 - 25 - 34 - - - 27 - 49 - 31 - - - 52 - 109 - 31 - - - 122 - 376 - 31 - - - 455 - 3337 - 15 - - - - - - - index - child - - - 12 - - - 1 - 2 - 31 - - - 2 - 3 - 37 - - - 3 - 4 - 9 - - - 4 - 5 - 72 - - - 5 - 8 - 31 - - - 8 - 10 - 31 - - - 10 - 17 - 34 - - - 17 - 25 - 34 - - - 27 - 49 - 31 - - - 52 - 109 - 31 - - - 122 - 376 - 31 - - - 455 - 3337 - 15 - - - - - - - child - ruby_module - - - 12 - - - 1 - 2 - 31303 - - - - - - - child - index - - - 12 - - - 1 - 2 - 31303 + 21511 @@ -15288,15 +14845,15 @@ ruby_module_def - 21557 + 21589 id - 21557 + 21589 name - 21557 + 21589 @@ -15310,7 +14867,7 @@ 1 2 - 21557 + 21589 @@ -15326,7 +14883,7 @@ 1 2 - 21557 + 21589 @@ -15336,15 +14893,15 @@ ruby_next_child - 240 + 245 ruby_next - 240 + 245 child - 240 + 245 @@ -15358,7 +14915,7 @@ 1 2 - 240 + 245 @@ -15374,7 +14931,7 @@ 1 2 - 240 + 245 @@ -15384,26 +14941,26 @@ ruby_next_def - 2005 + 2021 id - 2005 + 2021 ruby_operator_assignment_def - 6596 + 6502 id - 6596 + 6502 left - 6596 + 6502 operator @@ -15411,7 +14968,7 @@ right - 6596 + 6502 @@ -15425,7 +14982,7 @@ 1 2 - 6596 + 6502 @@ -15441,7 +14998,7 @@ 1 2 - 6596 + 6502 @@ -15457,7 +15014,7 @@ 1 2 - 6596 + 6502 @@ -15473,7 +15030,7 @@ 1 2 - 6596 + 6502 @@ -15489,7 +15046,7 @@ 1 2 - 6596 + 6502 @@ -15505,7 +15062,7 @@ 1 2 - 6596 + 6502 @@ -15534,18 +15091,18 @@ 2 - 61 - 62 + 62 + 63 2 - 592 - 593 + 594 + 595 2 - 1552 - 1553 + 1558 + 1559 2 @@ -15575,18 +15132,18 @@ 2 - 61 - 62 + 62 + 63 2 - 592 - 593 + 594 + 595 2 - 1552 - 1553 + 1558 + 1559 2 @@ -15616,18 +15173,18 @@ 2 - 61 - 62 + 62 + 63 2 - 592 - 593 + 594 + 595 2 - 1552 - 1553 + 1558 + 1559 2 @@ -15644,7 +15201,7 @@ 1 2 - 6596 + 6502 @@ -15660,7 +15217,7 @@ 1 2 - 6596 + 6502 @@ -15676,7 +15233,7 @@ 1 2 - 6596 + 6502 @@ -15686,19 +15243,19 @@ ruby_optional_parameter_def - 6435 + 6540 id - 6435 + 6540 name - 6435 + 6540 value - 6435 + 6540 @@ -15712,7 +15269,7 @@ 1 2 - 6435 + 6540 @@ -15728,7 +15285,7 @@ 1 2 - 6435 + 6540 @@ -15744,7 +15301,7 @@ 1 2 - 6435 + 6540 @@ -15760,7 +15317,7 @@ 1 2 - 6435 + 6540 @@ -15776,7 +15333,7 @@ 1 2 - 6435 + 6540 @@ -15792,7 +15349,7 @@ 1 2 - 6435 + 6540 @@ -15802,15 +15359,15 @@ ruby_pair_def - 235158 + 237637 id - 235158 + 237637 key__ - 235158 + 237637 @@ -15824,7 +15381,7 @@ 1 2 - 235158 + 237637 @@ -15840,7 +15397,7 @@ 1 2 - 235158 + 237637 @@ -15850,15 +15407,15 @@ ruby_pair_value - 235158 + 237637 ruby_pair - 235158 + 237637 value - 235158 + 237637 @@ -15872,7 +15429,7 @@ 1 2 - 235158 + 237637 @@ -15888,7 +15445,7 @@ 1 2 - 235158 + 237637 @@ -15940,11 +15497,11 @@ ruby_parenthesized_statements_child - 10272 + 10537 ruby_parenthesized_statements - 10208 + 10471 index @@ -15952,7 +15509,7 @@ child - 10272 + 10537 @@ -15966,12 +15523,12 @@ 1 2 - 10152 + 10413 2 5 - 56 + 58 @@ -15987,12 +15544,12 @@ 1 2 - 10152 + 10413 2 5 - 56 + 58 @@ -16016,13 +15573,13 @@ 1 - 56 - 57 + 58 + 59 1 - 10208 - 10209 + 10471 + 10472 1 @@ -16047,13 +15604,13 @@ 1 - 56 - 57 + 58 + 59 1 - 10208 - 10209 + 10471 + 10472 1 @@ -16070,7 +15627,7 @@ 1 2 - 10272 + 10537 @@ -16086,7 +15643,7 @@ 1 2 - 10272 + 10537 @@ -16096,26 +15653,26 @@ ruby_parenthesized_statements_def - 10247 + 10510 id - 10247 + 10510 ruby_pattern_def - 3895 + 3918 id - 3895 + 3918 child - 3895 + 3918 @@ -16129,7 +15686,7 @@ 1 2 - 3895 + 3918 @@ -16145,7 +15702,7 @@ 1 2 - 3895 + 3918 @@ -16155,19 +15712,19 @@ ruby_program_child - 33147 + 33068 ruby_program - 10456 + 10393 index - 236 + 235 child - 33147 + 33068 @@ -16181,32 +15738,32 @@ 1 2 - 3857 + 3809 2 3 - 2527 + 2513 3 4 - 1660 + 1661 4 5 - 791 + 796 5 8 - 910 + 895 8 - 76 - 709 + 77 + 716 @@ -16222,32 +15779,32 @@ 1 2 - 3857 + 3809 2 3 - 2527 + 2513 3 4 - 1660 + 1661 4 5 - 791 + 796 5 8 - 910 + 895 8 - 76 - 709 + 77 + 716 @@ -16263,7 +15820,7 @@ 1 2 - 50 + 52 2 @@ -16272,16 +15829,16 @@ 4 - 9 + 10 18 - 9 - 15 + 11 + 16 18 - 15 + 16 23 18 @@ -16291,23 +15848,23 @@ 18 - 38 - 63 + 39 + 64 18 68 - 139 + 141 18 - 157 - 515 + 159 + 521 18 - 765 - 3319 + 777 + 3354 12 @@ -16324,7 +15881,7 @@ 1 2 - 50 + 52 2 @@ -16333,16 +15890,16 @@ 4 - 9 + 10 18 - 9 - 15 + 11 + 16 18 - 15 + 16 23 18 @@ -16352,23 +15909,23 @@ 18 - 38 - 63 + 39 + 64 18 68 - 139 + 141 18 - 157 - 515 + 159 + 521 18 - 765 - 3319 + 777 + 3354 12 @@ -16385,7 +15942,7 @@ 1 2 - 33147 + 33068 @@ -16401,7 +15958,7 @@ 1 2 - 33147 + 33068 @@ -16411,26 +15968,26 @@ ruby_program_def - 17142 + 17193 id - 17142 + 17193 ruby_range_begin - 3997 + 4174 ruby_range - 3997 + 4174 begin - 3997 + 4174 @@ -16444,7 +16001,7 @@ 1 2 - 3997 + 4174 @@ -16460,7 +16017,7 @@ 1 2 - 3997 + 4174 @@ -16470,11 +16027,11 @@ ruby_range_def - 4187 + 4435 id - 4187 + 4435 operator @@ -16492,7 +16049,7 @@ 1 2 - 4187 + 4435 @@ -16506,13 +16063,13 @@ 12 - 1356 - 1357 + 1459 + 1460 1 - 2831 - 2832 + 2976 + 2977 1 @@ -16523,15 +16080,15 @@ ruby_range_end - 4070 + 4268 ruby_range - 4070 + 4268 end - 4070 + 4268 @@ -16545,7 +16102,7 @@ 1 2 - 4070 + 4268 @@ -16561,7 +16118,7 @@ 1 2 - 4070 + 4268 @@ -16571,15 +16128,15 @@ ruby_rational_def - 123 + 124 id - 123 + 124 child - 123 + 124 @@ -16593,7 +16150,7 @@ 1 2 - 123 + 124 @@ -16609,7 +16166,7 @@ 1 2 - 123 + 124 @@ -16678,19 +16235,19 @@ ruby_regex_child - 43301 + 43722 ruby_regex - 12839 + 13081 index - 154 + 151 child - 43301 + 43722 @@ -16704,42 +16261,42 @@ 1 2 - 6577 + 6732 2 3 - 696 + 712 3 4 - 1723 + 1745 4 5 - 466 + 492 5 6 - 1087 + 1094 6 8 - 1002 + 1016 8 15 - 986 + 995 15 50 - 299 + 291 @@ -16755,42 +16312,42 @@ 1 2 - 6577 + 6732 2 3 - 696 + 712 3 4 - 1723 + 1745 4 5 - 466 + 492 5 6 - 1087 + 1094 6 8 - 1002 + 1016 8 15 - 986 + 995 15 50 - 299 + 291 @@ -16830,17 +16387,17 @@ 15 - 18 + 19 12 - 18 - 22 + 19 + 23 9 - 22 - 30 + 23 + 31 12 @@ -16849,23 +16406,23 @@ 12 - 95 + 94 165 12 - 230 - 409 + 229 + 416 12 - 645 - 1220 + 658 + 1256 12 - 1766 - 4075 + 1818 + 4221 9 @@ -16906,17 +16463,17 @@ 15 - 18 + 19 12 - 18 - 22 + 19 + 23 9 - 22 - 30 + 23 + 31 12 @@ -16925,23 +16482,23 @@ 12 - 95 + 94 165 12 - 230 - 409 + 229 + 416 12 - 645 - 1220 + 658 + 1256 12 - 1766 - 4075 + 1818 + 4221 9 @@ -16958,7 +16515,7 @@ 1 2 - 43301 + 43722 @@ -16974,7 +16531,7 @@ 1 2 - 43301 + 43722 @@ -16984,26 +16541,26 @@ ruby_regex_def - 12854 + 13096 id - 12854 + 13096 ruby_rescue_body - 1767 + 1762 ruby_rescue - 1767 + 1762 body - 1767 + 1762 @@ -17017,7 +16574,7 @@ 1 2 - 1767 + 1762 @@ -17033,7 +16590,7 @@ 1 2 - 1767 + 1762 @@ -17043,26 +16600,26 @@ ruby_rescue_def - 2061 + 2085 id - 2061 + 2085 ruby_rescue_exceptions - 1639 + 1654 ruby_rescue - 1639 + 1654 exceptions - 1639 + 1654 @@ -17076,7 +16633,7 @@ 1 2 - 1639 + 1654 @@ -17092,7 +16649,7 @@ 1 2 - 1639 + 1654 @@ -17102,19 +16659,19 @@ ruby_rescue_modifier_def - 525 + 519 id - 525 + 519 body - 525 + 519 handler - 525 + 519 @@ -17128,7 +16685,7 @@ 1 2 - 525 + 519 @@ -17144,7 +16701,7 @@ 1 2 - 525 + 519 @@ -17160,7 +16717,7 @@ 1 2 - 525 + 519 @@ -17176,7 +16733,7 @@ 1 2 - 525 + 519 @@ -17192,7 +16749,7 @@ 1 2 - 525 + 519 @@ -17208,7 +16765,7 @@ 1 2 - 525 + 519 @@ -17218,15 +16775,15 @@ ruby_rescue_variable - 1001 + 991 ruby_rescue - 1001 + 991 variable - 1001 + 991 @@ -17240,7 +16797,7 @@ 1 2 - 1001 + 991 @@ -17256,7 +16813,7 @@ 1 2 - 1001 + 991 @@ -17314,11 +16871,11 @@ ruby_rest_assignment_def - 398 + 399 id - 398 + 399 @@ -17373,26 +16930,26 @@ ruby_retry_def - 56 + 59 id - 56 + 59 ruby_return_child - 5336 + 5330 ruby_return - 5336 + 5330 child - 5336 + 5330 @@ -17406,7 +16963,7 @@ 1 2 - 5336 + 5330 @@ -17422,7 +16979,7 @@ 1 2 - 5336 + 5330 @@ -17432,22 +16989,22 @@ ruby_return_def - 8495 + 8486 id - 8495 + 8486 ruby_right_assignment_list_child - 2757 + 2721 ruby_right_assignment_list - 1288 + 1274 index @@ -17455,7 +17012,7 @@ child - 2757 + 2721 @@ -17469,17 +17026,17 @@ 2 3 - 1147 + 1134 3 4 - 110 + 111 4 6 - 31 + 27 @@ -17495,17 +17052,17 @@ 2 3 - 1147 + 1134 3 4 - 110 + 111 4 6 - 31 + 27 @@ -17524,8 +17081,8 @@ 3 - 10 - 11 + 9 + 10 3 @@ -17534,8 +17091,8 @@ 3 - 409 - 410 + 411 + 412 6 @@ -17555,8 +17112,8 @@ 3 - 10 - 11 + 9 + 10 3 @@ -17565,8 +17122,8 @@ 3 - 409 - 410 + 411 + 412 6 @@ -17583,7 +17140,7 @@ 1 2 - 2757 + 2721 @@ -17599,7 +17156,7 @@ 1 2 - 2757 + 2721 @@ -17609,26 +17166,26 @@ ruby_right_assignment_list_def - 1288 + 1274 id - 1288 + 1274 ruby_scope_resolution_def - 80009 + 81024 id - 80009 + 81024 name - 80009 + 81024 @@ -17642,7 +17199,7 @@ 1 2 - 80009 + 81024 @@ -17658,7 +17215,7 @@ 1 2 - 80009 + 81024 @@ -17668,15 +17225,15 @@ ruby_scope_resolution_scope - 78256 + 79203 ruby_scope_resolution - 78256 + 79203 scope - 78256 + 79203 @@ -17690,7 +17247,7 @@ 1 2 - 78256 + 79203 @@ -17706,7 +17263,7 @@ 1 2 - 78256 + 79203 @@ -17716,15 +17273,15 @@ ruby_setter_def - 598 + 601 id - 598 + 601 name - 598 + 601 @@ -17738,7 +17295,7 @@ 1 2 - 598 + 601 @@ -17754,7 +17311,7 @@ 1 2 - 598 + 601 @@ -17763,26 +17320,22 @@ - ruby_singleton_class_child - 2322 + ruby_singleton_class_body + 635 ruby_singleton_class - 620 + 635 - index - 72 - - - child - 2322 + body + 635 ruby_singleton_class - index + body 12 @@ -17790,100 +17343,14 @@ 1 2 - 283 - - - 2 - 3 - 81 - - - 3 - 4 - 40 - - - 4 - 5 - 40 - - - 5 - 6 - 40 - - - 6 - 8 - 44 - - - 8 - 13 - 53 - - - 13 - 24 - 34 + 635 - ruby_singleton_class - child - - - 12 - - - 1 - 2 - 283 - - - 2 - 3 - 81 - - - 3 - 4 - 40 - - - 4 - 5 - 40 - - - 5 - 6 - 40 - - - 6 - 8 - 44 - - - 8 - 13 - 53 - - - 13 - 24 - 34 - - - - - - - index + body ruby_singleton_class @@ -17892,155 +17359,7 @@ 1 2 - 3 - - - 2 - 3 - 9 - - - 3 - 4 - 9 - - - 6 - 8 - 6 - - - 9 - 12 - 6 - - - 16 - 20 - 6 - - - 21 - 23 - 6 - - - 28 - 33 - 6 - - - 42 - 56 - 6 - - - 68 - 82 - 6 - - - 107 - 198 - 6 - - - - - - - index - child - - - 12 - - - 1 - 2 - 3 - - - 2 - 3 - 9 - - - 3 - 4 - 9 - - - 6 - 8 - 6 - - - 9 - 12 - 6 - - - 16 - 20 - 6 - - - 21 - 23 - 6 - - - 28 - 33 - 6 - - - 42 - 56 - 6 - - - 68 - 82 - 6 - - - 107 - 198 - 6 - - - - - - - child - ruby_singleton_class - - - 12 - - - 1 - 2 - 2322 - - - - - - - child - index - - - 12 - - - 1 - 2 - 2322 + 635 @@ -18050,15 +17369,15 @@ ruby_singleton_class_def - 620 + 635 id - 620 + 635 value - 620 + 635 @@ -18072,7 +17391,7 @@ 1 2 - 620 + 635 @@ -18088,7 +17407,7 @@ 1 2 - 620 + 635 @@ -18097,26 +17416,22 @@ - ruby_singleton_method_child - 16018 + ruby_singleton_method_body + 6677 ruby_singleton_method - 6539 + 6677 - index - 89 - - - child - 16018 + body + 6677 ruby_singleton_method - index + body 12 @@ -18124,80 +17439,14 @@ 1 2 - 3714 - - - 2 - 3 - 965 - - - 3 - 4 - 612 - - - 4 - 5 - 374 - - - 5 - 8 - 505 - - - 8 - 31 - 368 + 6677 - ruby_singleton_method - child - - - 12 - - - 1 - 2 - 3714 - - - 2 - 3 - 965 - - - 3 - 4 - 612 - - - 4 - 5 - 374 - - - 5 - 8 - 505 - - - 8 - 31 - 368 - - - - - - - index + body ruby_singleton_method @@ -18206,195 +17455,7 @@ 1 2 - 2 - - - 2 - 3 - 11 - - - 3 - 4 - 5 - - - 4 - 5 - 2 - - - 5 - 6 - 8 - - - 9 - 10 - 5 - - - 12 - 17 - 5 - - - 23 - 28 - 5 - - - 32 - 41 - 5 - - - 51 - 66 - 5 - - - 93 - 125 - 5 - - - 155 - 210 - 5 - - - 294 - 421 - 5 - - - 626 - 952 - 5 - - - 2201 - 2202 - 2 - - - - - - - index - child - - - 12 - - - 1 - 2 - 2 - - - 2 - 3 - 11 - - - 3 - 4 - 5 - - - 4 - 5 - 2 - - - 5 - 6 - 8 - - - 9 - 10 - 5 - - - 12 - 17 - 5 - - - 23 - 28 - 5 - - - 32 - 41 - 5 - - - 51 - 66 - 5 - - - 93 - 125 - 5 - - - 155 - 210 - 5 - - - 294 - 421 - 5 - - - 626 - 952 - 5 - - - 2201 - 2202 - 2 - - - - - - - child - ruby_singleton_method - - - 12 - - - 1 - 2 - 16018 - - - - - - - child - index - - - 12 - - - 1 - 2 - 16018 + 6677 @@ -18404,19 +17465,19 @@ ruby_singleton_method_def - 6539 + 6692 id - 6539 + 6692 name - 6539 + 6692 object - 6539 + 6692 @@ -18430,7 +17491,7 @@ 1 2 - 6539 + 6692 @@ -18446,7 +17507,7 @@ 1 2 - 6539 + 6692 @@ -18462,7 +17523,7 @@ 1 2 - 6539 + 6692 @@ -18478,7 +17539,7 @@ 1 2 - 6539 + 6692 @@ -18494,7 +17555,7 @@ 1 2 - 6539 + 6692 @@ -18510,7 +17571,7 @@ 1 2 - 6539 + 6692 @@ -18520,15 +17581,15 @@ ruby_singleton_method_parameters - 4082 + 4183 ruby_singleton_method - 4082 + 4183 parameters - 4082 + 4183 @@ -18542,7 +17603,7 @@ 1 2 - 4082 + 4183 @@ -18558,7 +17619,7 @@ 1 2 - 4082 + 4183 @@ -18568,15 +17629,15 @@ ruby_splat_argument_def - 3046 + 3093 id - 3046 + 3093 child - 3046 + 3093 @@ -18590,7 +17651,7 @@ 1 2 - 3046 + 3093 @@ -18606,7 +17667,7 @@ 1 2 - 3046 + 3093 @@ -18616,26 +17677,26 @@ ruby_splat_parameter_def - 2905 + 2978 id - 2905 + 2978 ruby_splat_parameter_name - 2354 + 2383 ruby_splat_parameter - 2354 + 2383 name - 2354 + 2383 @@ -18649,7 +17710,7 @@ 1 2 - 2354 + 2383 @@ -18665,7 +17726,7 @@ 1 2 - 2354 + 2383 @@ -18675,11 +17736,11 @@ ruby_string_array_child - 11487 + 11622 ruby_string_array - 3704 + 3773 index @@ -18687,7 +17748,7 @@ child - 11487 + 11622 @@ -18701,27 +17762,27 @@ 1 2 - 1200 + 1222 2 3 - 1218 + 1256 3 4 - 576 + 581 4 5 - 294 + 296 5 10 - 282 + 284 10 @@ -18742,27 +17803,27 @@ 1 2 - 1200 + 1222 2 3 - 1218 + 1256 3 4 - 576 + 581 4 5 - 294 + 296 5 10 - 282 + 284 10 @@ -18792,12 +17853,12 @@ 17 - 417 + 419 35 - 710 - 3705 + 714 + 3774 4 @@ -18823,12 +17884,12 @@ 17 - 417 + 419 35 - 710 - 3705 + 714 + 3774 4 @@ -18845,7 +17906,7 @@ 1 2 - 11487 + 11622 @@ -18861,7 +17922,7 @@ 1 2 - 11487 + 11622 @@ -18871,22 +17932,22 @@ ruby_string_array_def - 3840 + 3913 id - 3840 + 3913 ruby_string_child - 534498 + 538306 ruby_string__ - 466613 + 469399 index @@ -18894,7 +17955,7 @@ child - 534498 + 538306 @@ -18908,12 +17969,12 @@ 1 2 - 439982 + 442362 2 282 - 26631 + 27037 @@ -18929,12 +17990,12 @@ 1 2 - 439982 + 442362 2 282 - 26631 + 27037 @@ -18974,7 +18035,7 @@ 102 - 466614 + 469400 22 @@ -19015,7 +18076,7 @@ 102 - 466614 + 469400 22 @@ -19032,7 +18093,7 @@ 1 2 - 534498 + 538306 @@ -19048,7 +18109,7 @@ 1 2 - 534498 + 538306 @@ -19058,22 +18119,22 @@ ruby_string_def - 473533 + 476418 id - 473533 + 476418 ruby_subshell_child - 620 + 604 ruby_subshell - 403 + 393 index @@ -19081,7 +18142,7 @@ child - 620 + 604 @@ -19095,22 +18156,22 @@ 1 2 - 296 + 288 2 3 - 59 + 58 3 4 - 18 + 21 4 12 - 28 + 24 @@ -19126,22 +18187,22 @@ 1 2 - 296 + 288 2 3 - 59 + 58 3 4 - 18 + 21 4 12 - 28 + 24 @@ -19170,8 +18231,8 @@ 3 - 9 - 10 + 8 + 9 3 @@ -19185,8 +18246,8 @@ 3 - 128 - 129 + 127 + 128 3 @@ -19216,8 +18277,8 @@ 3 - 9 - 10 + 8 + 9 3 @@ -19231,8 +18292,8 @@ 3 - 128 - 129 + 127 + 128 3 @@ -19249,7 +18310,7 @@ 1 2 - 620 + 604 @@ -19265,7 +18326,7 @@ 1 2 - 620 + 604 @@ -19275,26 +18336,26 @@ ruby_subshell_def - 403 + 393 id - 403 + 393 ruby_superclass_def - 13318 + 13378 id - 13318 + 13378 child - 13318 + 13378 @@ -19308,7 +18369,7 @@ 1 2 - 13318 + 13378 @@ -19324,7 +18385,7 @@ 1 2 - 13318 + 13378 @@ -19334,19 +18395,19 @@ ruby_symbol_array_child - 2106 + 2085 ruby_symbol_array - 463 + 460 index - 98 + 96 child - 2106 + 2085 @@ -19360,17 +18421,17 @@ 1 2 - 184 + 180 2 3 - 92 + 93 3 4 - 38 + 40 4 @@ -19385,12 +18446,12 @@ 8 13 - 38 + 37 13 22 - 38 + 37 27 @@ -19411,17 +18472,17 @@ 1 2 - 184 + 180 2 3 - 92 + 93 3 4 - 38 + 40 4 @@ -19436,12 +18497,12 @@ 8 13 - 38 + 37 13 22 - 38 + 37 27 @@ -19462,12 +18523,12 @@ 1 2 - 11 + 8 2 3 - 5 + 8 3 @@ -19505,8 +18566,8 @@ 8 - 63 - 157 + 64 + 159 8 @@ -19523,12 +18584,12 @@ 1 2 - 11 + 8 2 3 - 5 + 8 3 @@ -19566,8 +18627,8 @@ 8 - 63 - 157 + 64 + 159 8 @@ -19584,7 +18645,7 @@ 1 2 - 2106 + 2085 @@ -19600,7 +18661,7 @@ 1 2 - 2106 + 2085 @@ -19610,30 +18671,30 @@ ruby_symbol_array_def - 463 + 460 id - 463 + 460 ruby_then_child - 41375 + 40747 ruby_then - 24329 + 24194 index - 288 + 105 child - 41375 + 40747 @@ -19647,22 +18708,22 @@ 1 2 - 15210 + 15103 2 3 - 5479 + 5505 3 4 - 2011 + 1954 4 - 98 - 1628 + 37 + 1630 @@ -19678,22 +18739,22 @@ 1 2 - 15210 + 15103 2 3 - 5479 + 5505 3 4 - 2011 + 1954 4 - 98 - 1628 + 37 + 1630 @@ -19709,33 +18770,48 @@ 1 2 - 184 + 35 2 4 - 20 + 8 4 - 7 - 20 - - - 7 - 12 - 23 - - - 12 - 157 - 23 - - - 292 - 8189 + 5 14 + + 6 + 10 + 8 + + + 10 + 20 + 8 + + + 30 + 59 + 8 + + + 91 + 293 + 8 + + + 559 + 3117 + 8 + + + 8293 + 8294 + 2 + @@ -19750,33 +18826,48 @@ 1 2 - 184 + 35 2 4 - 20 + 8 4 - 7 - 20 - - - 7 - 12 - 23 - - - 12 - 157 - 23 - - - 292 - 8189 + 5 14 + + 6 + 10 + 8 + + + 10 + 20 + 8 + + + 30 + 59 + 8 + + + 91 + 293 + 8 + + + 559 + 3117 + 8 + + + 8293 + 8294 + 2 + @@ -19791,7 +18882,7 @@ 1 2 - 41375 + 40747 @@ -19807,7 +18898,7 @@ 1 2 - 41375 + 40747 @@ -19817,30 +18908,30 @@ ruby_then_def - 24329 + 24194 id - 24329 + 24194 ruby_tokeninfo - 5932219 + 5993849 id - 5932219 + 5993849 kind - 65 + 64 value - 268924 + 272090 @@ -19854,7 +18945,7 @@ 1 2 - 5932219 + 5993849 @@ -19870,7 +18961,7 @@ 1 2 - 5932219 + 5993849 @@ -19884,65 +18975,65 @@ 12 - 36 - 154 + 38 + 162 5 - 218 + 229 427 5 - 1610 - 1611 + 1651 + 1652 2 - 1805 - 1806 + 1892 + 1893 5 - 3990 - 4222 - 5 - - - 4225 - 5692 - 5 - - - 7841 - 9805 - 5 - - - 13552 - 17172 - 5 - - - 25328 - 54391 - 5 - - - 55573 - 79435 - 5 - - - 95059 - 499129 - 5 - - - 1115017 - 1115018 + 4175 + 4176 2 + + 4361 + 4362 + 5 + + + 5941 + 8156 + 5 + + + 9961 + 13707 + 5 + + + 17937 + 25691 + 5 + + + 55821 + 57168 + 5 + + + 82456 + 97584 + 5 + + + 513548 + 1147355 + 5 + @@ -19961,47 +19052,47 @@ 5 - 22 + 24 5 25 - 30 + 32 5 - 69 - 123 + 71 + 122 5 - 123 + 124 148 5 - 1480 - 1754 + 1483 + 1800 5 - 3052 - 3730 + 3157 + 3799 5 - 4632 - 7672 + 4735 + 7872 5 - 10019 - 18637 + 10529 + 19137 5 - 44586 - 44587 + 45798 + 45799 2 @@ -20018,32 +19109,32 @@ 1 2 - 159668 + 162126 2 3 - 39566 + 39738 3 4 - 18844 + 18995 4 7 - 22228 + 22513 7 27 - 20374 + 20459 27 - 183191 - 8242 + 189123 + 8256 @@ -20059,12 +19150,12 @@ 1 2 - 255594 + 259066 2 5 - 13329 + 13023 @@ -20074,15 +19165,15 @@ ruby_unary_def - 13057 + 13450 id - 13057 + 13450 operand - 13057 + 13450 operator @@ -20100,7 +19191,7 @@ 1 2 - 13057 + 13450 @@ -20116,7 +19207,7 @@ 1 2 - 13057 + 13450 @@ -20132,7 +19223,7 @@ 1 2 - 13057 + 13450 @@ -20148,7 +19239,7 @@ 1 2 - 13057 + 13450 @@ -20162,33 +19253,33 @@ 12 - 88 - 89 + 89 + 90 1 - 236 - 237 + 190 + 191 1 - 557 - 558 + 559 + 560 1 - 1312 - 1313 + 1320 + 1321 1 - 1742 - 1743 + 1820 + 1821 1 - 9122 - 9123 + 9472 + 9473 1 @@ -20203,33 +19294,33 @@ 12 - 88 - 89 + 89 + 90 1 - 236 - 237 + 190 + 191 1 - 557 - 558 + 559 + 560 1 - 1312 - 1313 + 1320 + 1321 1 - 1742 - 1743 + 1820 + 1821 1 - 9122 - 9123 + 9472 + 9473 1 @@ -20240,11 +19331,11 @@ ruby_undef_child - 181 + 182 ruby_undef - 180 + 181 index @@ -20252,7 +19343,7 @@ child - 181 + 182 @@ -20266,7 +19357,7 @@ 1 2 - 179 + 180 2 @@ -20287,7 +19378,7 @@ 1 2 - 179 + 180 2 @@ -20311,8 +19402,8 @@ 1 - 180 - 181 + 181 + 182 1 @@ -20332,8 +19423,8 @@ 1 - 180 - 181 + 181 + 182 1 @@ -20350,7 +19441,7 @@ 1 2 - 181 + 182 @@ -20366,7 +19457,7 @@ 1 2 - 181 + 182 @@ -20376,11 +19467,11 @@ ruby_undef_def - 180 + 181 id - 180 + 181 @@ -20435,15 +19526,15 @@ ruby_unless_consequence - 2575 + 2593 ruby_unless - 2575 + 2593 consequence - 2575 + 2593 @@ -20457,7 +19548,7 @@ 1 2 - 2575 + 2593 @@ -20473,7 +19564,7 @@ 1 2 - 2575 + 2593 @@ -20483,15 +19574,15 @@ ruby_unless_def - 2578 + 2594 id - 2578 + 2594 condition - 2578 + 2594 @@ -20505,7 +19596,7 @@ 1 2 - 2578 + 2594 @@ -20521,7 +19612,7 @@ 1 2 - 2578 + 2594 @@ -20573,19 +19664,19 @@ ruby_unless_modifier_def - 4207 + 4163 id - 4207 + 4163 body - 4207 + 4163 condition - 4207 + 4163 @@ -20599,7 +19690,7 @@ 1 2 - 4207 + 4163 @@ -20615,7 +19706,7 @@ 1 2 - 4207 + 4163 @@ -20631,7 +19722,7 @@ 1 2 - 4207 + 4163 @@ -20647,7 +19738,7 @@ 1 2 - 4207 + 4163 @@ -20663,7 +19754,7 @@ 1 2 - 4207 + 4163 @@ -20679,7 +19770,7 @@ 1 2 - 4207 + 4163 @@ -20689,19 +19780,19 @@ ruby_until_def - 113 + 121 id - 113 + 121 body - 113 + 121 condition - 113 + 121 @@ -20715,7 +19806,7 @@ 1 2 - 113 + 121 @@ -20731,7 +19822,7 @@ 1 2 - 113 + 121 @@ -20747,7 +19838,7 @@ 1 2 - 113 + 121 @@ -20763,7 +19854,7 @@ 1 2 - 113 + 121 @@ -20779,7 +19870,7 @@ 1 2 - 113 + 121 @@ -20795,7 +19886,7 @@ 1 2 - 113 + 121 @@ -20805,19 +19896,19 @@ ruby_until_modifier_def - 206 + 223 id - 206 + 223 body - 206 + 223 condition - 206 + 223 @@ -20831,7 +19922,7 @@ 1 2 - 206 + 223 @@ -20847,7 +19938,7 @@ 1 2 - 206 + 223 @@ -20863,7 +19954,7 @@ 1 2 - 206 + 223 @@ -20879,7 +19970,7 @@ 1 2 - 206 + 223 @@ -20895,7 +19986,7 @@ 1 2 - 206 + 223 @@ -20911,7 +20002,7 @@ 1 2 - 206 + 223 @@ -20963,15 +20054,15 @@ ruby_when_body - 3208 + 3211 ruby_when - 3208 + 3211 body - 3208 + 3211 @@ -20985,7 +20076,7 @@ 1 2 - 3208 + 3211 @@ -21001,7 +20092,7 @@ 1 2 - 3208 + 3211 @@ -21011,30 +20102,30 @@ ruby_when_def - 3239 + 3248 id - 3239 + 3248 ruby_when_pattern - 3895 + 3918 ruby_when - 3239 + 3248 index - 44 + 43 pattern - 3895 + 3918 @@ -21053,12 +20144,12 @@ 2 3 - 305 + 309 3 15 - 103 + 108 @@ -21079,12 +20170,12 @@ 2 3 - 305 + 309 3 15 - 103 + 108 @@ -21118,23 +20209,23 @@ 3 - 12 - 13 + 13 + 14 3 - 33 - 34 + 35 + 36 3 - 130 - 131 + 135 + 136 3 - 1028 - 1029 + 1048 + 1049 3 @@ -21169,23 +20260,23 @@ 3 - 12 - 13 + 13 + 14 3 - 33 - 34 + 35 + 36 3 - 130 - 131 + 135 + 136 3 - 1028 - 1029 + 1048 + 1049 3 @@ -21202,7 +20293,7 @@ 1 2 - 3895 + 3918 @@ -21218,7 +20309,7 @@ 1 2 - 3895 + 3918 @@ -21228,19 +20319,19 @@ ruby_while_def - 1335 + 1349 id - 1335 + 1349 body - 1335 + 1349 condition - 1335 + 1349 @@ -21254,7 +20345,7 @@ 1 2 - 1335 + 1349 @@ -21270,7 +20361,7 @@ 1 2 - 1335 + 1349 @@ -21286,7 +20377,7 @@ 1 2 - 1335 + 1349 @@ -21302,7 +20393,7 @@ 1 2 - 1335 + 1349 @@ -21318,7 +20409,7 @@ 1 2 - 1335 + 1349 @@ -21334,7 +20425,7 @@ 1 2 - 1335 + 1349 @@ -21344,19 +20435,19 @@ ruby_while_modifier_def - 179 + 190 id - 179 + 190 body - 179 + 190 condition - 179 + 190 @@ -21370,7 +20461,7 @@ 1 2 - 179 + 190 @@ -21386,7 +20477,7 @@ 1 2 - 179 + 190 @@ -21402,7 +20493,7 @@ 1 2 - 179 + 190 @@ -21418,7 +20509,7 @@ 1 2 - 179 + 190 @@ -21434,7 +20525,7 @@ 1 2 - 179 + 190 @@ -21450,7 +20541,7 @@ 1 2 - 179 + 190 @@ -21460,15 +20551,15 @@ ruby_yield_child - 1112 + 1097 ruby_yield - 1112 + 1097 child - 1112 + 1097 @@ -21482,7 +20573,7 @@ 1 2 - 1112 + 1097 @@ -21498,7 +20589,7 @@ 1 2 - 1112 + 1097 @@ -21508,22 +20599,22 @@ ruby_yield_def - 2385 + 2389 id - 2385 + 2389 sourceLocationPrefix - 12 + 13 prefix - 12 + 13 diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/locations_default.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/locations_default.ql new file mode 100644 index 00000000000..e98f74d7bf1 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/locations_default.ql @@ -0,0 +1,68 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +class Location extends @location { + string toString() { none() } +} + +class File extends @file { + string toString() { none() } +} + +private predicate body_statement(AstNode body, int index, Location loc) { + exists(AstNode node, AstNode child | ruby_ast_node_info(child, _, _, loc) | + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, index, child) + or + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, index, child) + or + ruby_method_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, index, child) + or + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, index, child) + or + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, index, child) + or + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, index, child) + or + ruby_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(node, index, child) + ) +} + +from Location loc, File file, int start_line, int start_column, int end_line, int end_column +where + locations_default(loc, file, start_line, start_column, end_line, end_column) and + not exists(AstNode node | ruby_ast_node_info(node, _, _, loc) and body_statement(node, _, _)) + or + exists(AstNode node | + ruby_ast_node_info(node, _, _, loc) and + exists(Location first | + body_statement(node, 0, first) and + locations_default(first, pragma[only_bind_into](file), start_line, start_column, _, _) + ) and + exists(Location last | + last = max(Location l, int i | body_statement(node, i, l) | l order by i) and + locations_default(last, pragma[only_bind_into](file), _, _, end_line, end_column) + ) + ) +select loc, file, start_line, start_column, end_line, end_column diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/old.dbscheme b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/old.dbscheme new file mode 100644 index 00000000000..4ba51641799 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/old.dbscheme @@ -0,0 +1,1433 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_call_operator = @ruby_reserved_word + +@ruby_underscore_expression = @ruby_assignment | @ruby_binary | @ruby_break | @ruby_call | @ruby_next | @ruby_operator_assignment | @ruby_return | @ruby_unary | @ruby_underscore_arg | @ruby_yield + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable + +@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable + +@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant + +@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic + +@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern + +@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_heredoc_beginning | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric + +@ruby_underscore_pattern_top_expr_body = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_underscore_pattern_expr + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_heredoc_beginning | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_underscore_simple_numeric | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_simple_numeric = @ruby_complex | @ruby_rational | @ruby_token_float | @ruby_token_integer + +@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier + +@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref +); + +#keyset[ruby_alternative_pattern, index] +ruby_alternative_pattern_alternatives( + int ruby_alternative_pattern: @ruby_alternative_pattern ref, + int index: int ref, + unique int alternatives: @ruby_underscore_pattern_expr_basic ref +); + +ruby_alternative_pattern_def( + unique int id: @ruby_alternative_pattern +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array +); + +ruby_array_pattern_class( + unique int ruby_array_pattern: @ruby_array_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_array_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_array_pattern, index] +ruby_array_pattern_child( + int ruby_array_pattern: @ruby_array_pattern ref, + int index: int ref, + unique int child: @ruby_array_pattern_child_type ref +); + +ruby_array_pattern_def( + unique int id: @ruby_array_pattern +); + +ruby_as_pattern_def( + unique int id: @ruby_as_pattern, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_pattern_expr ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_rescue_modifier | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_expression + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block +); + +@ruby_binary_left_type = @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_underscore_expression ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block, index] +ruby_block_child( + int ruby_block: @ruby_block ref, + int index: int ref, + unique int child: @ruby_block_child_type ref +); + +ruby_block_def( + unique int id: @ruby_block +); + +ruby_block_argument_child( + unique int ruby_block_argument: @ruby_block_argument ref, + unique int child: @ruby_underscore_arg ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument +); + +ruby_block_parameter_name( + unique int ruby_block_parameter: @ruby_block_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter +); + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_locals( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int locals: @ruby_token_identifier ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_token_operator | @ruby_underscore_variable + +ruby_call_method( + unique int ruby_call: @ruby_call ref, + unique int method: @ruby_call_method_type ref +); + +ruby_call_operator( + unique int ruby_call: @ruby_call ref, + unique int operator: @ruby_underscore_call_operator ref +); + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_underscore_primary ref +); + +ruby_call_def( + unique int id: @ruby_call +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__ +); + +#keyset[ruby_case_match, index] +ruby_case_match_clauses( + int ruby_case_match: @ruby_case_match ref, + int index: int ref, + unique int clauses: @ruby_in_clause ref +); + +ruby_case_match_else( + unique int ruby_case_match: @ruby_case_match ref, + unique int else: @ruby_else ref +); + +ruby_case_match_def( + unique int id: @ruby_case_match, + int value: @ruby_underscore_statement ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +@ruby_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_class, index] +ruby_class_child( + int ruby_class: @ruby_class ref, + int index: int ref, + unique int child: @ruby_class_child_type ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref +); + +@ruby_complex_child_type = @ruby_rational | @ruby_token_float | @ruby_token_integer + +ruby_complex_def( + unique int id: @ruby_complex, + int child: @ruby_complex_child_type ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +@ruby_do_block_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do_block, index] +ruby_do_block_child( + int ruby_do_block: @ruby_do_block ref, + int index: int ref, + unique int child: @ruby_do_block_child_type ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions +); + +ruby_expression_reference_pattern_def( + unique int id: @ruby_expression_reference_pattern, + int value: @ruby_underscore_expression ref +); + +ruby_find_pattern_class( + unique int ruby_find_pattern: @ruby_find_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_find_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_find_pattern, index] +ruby_find_pattern_child( + int ruby_find_pattern: @ruby_find_pattern ref, + int index: int ref, + unique int child: @ruby_find_pattern_child_type ref +); + +ruby_find_pattern_def( + unique int id: @ruby_find_pattern +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash +); + +ruby_hash_pattern_class( + unique int ruby_hash_pattern: @ruby_hash_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_hash_pattern_child_type = @ruby_hash_splat_parameter | @ruby_keyword_pattern | @ruby_token_hash_splat_nil + +#keyset[ruby_hash_pattern, index] +ruby_hash_pattern_child( + int ruby_hash_pattern: @ruby_hash_pattern ref, + int index: int ref, + unique int child: @ruby_hash_pattern_child_type ref +); + +ruby_hash_pattern_def( + unique int id: @ruby_hash_pattern +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref +); + +ruby_if_guard_def( + unique int id: @ruby_if_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref +); + +ruby_in_clause_body( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int body: @ruby_then ref +); + +@ruby_in_clause_guard_type = @ruby_if_guard | @ruby_unless_guard + +ruby_in_clause_guard( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int guard: @ruby_in_clause_guard_type ref +); + +ruby_in_clause_def( + unique int id: @ruby_in_clause, + int pattern: @ruby_underscore_pattern_top_expr_body ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref +); + +@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol + +ruby_keyword_pattern_value( + unique int ruby_keyword_pattern: @ruby_keyword_pattern ref, + unique int value: @ruby_underscore_pattern_expr ref +); + +ruby_keyword_pattern_def( + unique int id: @ruby_keyword_pattern, + int key__: @ruby_keyword_pattern_key_type ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement + +#keyset[ruby_method, index] +ruby_method_child( + int ruby_method: @ruby_method ref, + int index: int ref, + unique int child: @ruby_method_child_type ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +@ruby_module_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_module, index] +ruby_module_child( + int ruby_module: @ruby_module ref, + int index: int ref, + unique int child: @ruby_module_child_type ref +); + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_rescue_modifier | @ruby_underscore_expression + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_value( + unique int ruby_pair: @ruby_pair ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref +); + +ruby_parenthesized_pattern_def( + unique int id: @ruby_parenthesized_pattern, + int child: @ruby_underscore_pattern_expr ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program +); + +@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_range_begin_type ref +); + +@ruby_range_end_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_range_end_type ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue +); + +@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_rescue_modifier_body_type ref, + int handler: @ruby_underscore_expression ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list +); + +@ruby_scope_resolution_scope_type = @ruby_underscore_pattern_constant | @ruby_underscore_primary + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_scope_resolution_scope_type ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_token_constant ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref +); + +@ruby_singleton_class_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_singleton_class, index] +ruby_singleton_class_child( + int ruby_singleton_class: @ruby_singleton_class ref, + int index: int ref, + unique int child: @ruby_singleton_class_child_type ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +@ruby_singleton_method_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_arg | @ruby_underscore_statement + +#keyset[ruby_singleton_method, index] +ruby_singleton_method_child( + int ruby_singleton_method: @ruby_singleton_method ref, + int index: int ref, + unique int child: @ruby_singleton_method_child_type ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__ +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell +); + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_underscore_expression ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then +); + +@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref +); + +ruby_unless_guard_def( + unique int id: @ruby_unless_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable + +ruby_variable_reference_pattern_def( + unique int id: @ruby_variable_reference_pattern, + int name: @ruby_variable_reference_pattern_name_type ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_constant +| 5 = @ruby_token_empty_statement +| 6 = @ruby_token_encoding +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_file +| 10 = @ruby_token_float +| 11 = @ruby_token_forward_argument +| 12 = @ruby_token_forward_parameter +| 13 = @ruby_token_global_variable +| 14 = @ruby_token_hash_key_symbol +| 15 = @ruby_token_hash_splat_nil +| 16 = @ruby_token_heredoc_beginning +| 17 = @ruby_token_heredoc_content +| 18 = @ruby_token_heredoc_end +| 19 = @ruby_token_identifier +| 20 = @ruby_token_instance_variable +| 21 = @ruby_token_integer +| 22 = @ruby_token_line +| 23 = @ruby_token_nil +| 24 = @ruby_token_operator +| 25 = @ruby_token_self +| 26 = @ruby_token_simple_symbol +| 27 = @ruby_token_string_content +| 28 = @ruby_token_super +| 29 = @ruby_token_true +| 30 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_parameter | @ruby_block_parameters | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_info( + unique int node: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + +erb_comment_directive_child( + unique int erb_comment_directive: @erb_comment_directive ref, + unique int child: @erb_token_comment ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive +); + +erb_directive_child( + unique int erb_directive: @erb_directive ref, + unique int child: @erb_token_code ref +); + +erb_directive_def( + unique int id: @erb_directive +); + +erb_graphql_directive_child( + unique int erb_graphql_directive: @erb_graphql_directive ref, + unique int child: @erb_token_code ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive +); + +erb_output_directive_child( + unique int erb_output_directive: @erb_output_directive ref, + unique int child: @erb_token_code ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_info( + unique int node: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby.dbscheme b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby.dbscheme new file mode 100644 index 00000000000..3595c826de6 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby.dbscheme @@ -0,0 +1,1435 @@ +// CodeQL database schema for Ruby +// Automatically generated from the tree-sitter grammar; do not edit + +@location = @location_default + +locations_default( + unique int id: @location_default, + int file: @file ref, + int start_line: int ref, + int start_column: int ref, + int end_line: int ref, + int end_column: int ref +); + +files( + unique int id: @file, + string name: string ref +); + +folders( + unique int id: @folder, + string name: string ref +); + +@container = @file | @folder + +containerparent( + int parent: @container ref, + unique int child: @container ref +); + +sourceLocationPrefix( + string prefix: string ref +); + +diagnostics( + unique int id: @diagnostic, + int severity: int ref, + string error_tag: string ref, + string error_message: string ref, + string full_error_message: string ref, + int location: @location_default ref +); + +case @diagnostic.severity of + 10 = @diagnostic_debug +| 20 = @diagnostic_info +| 30 = @diagnostic_warning +| 40 = @diagnostic_error +; + + +@ruby_underscore_arg = @ruby_assignment | @ruby_binary | @ruby_conditional | @ruby_operator_assignment | @ruby_range | @ruby_unary | @ruby_underscore_primary + +@ruby_underscore_call_operator = @ruby_reserved_word + +@ruby_underscore_expression = @ruby_assignment | @ruby_binary | @ruby_break | @ruby_call | @ruby_next | @ruby_operator_assignment | @ruby_return | @ruby_unary | @ruby_underscore_arg | @ruby_yield + +@ruby_underscore_lhs = @ruby_call | @ruby_element_reference | @ruby_scope_resolution | @ruby_token_false | @ruby_token_nil | @ruby_token_true | @ruby_underscore_variable + +@ruby_underscore_method_name = @ruby_delimited_symbol | @ruby_setter | @ruby_token_constant | @ruby_token_identifier | @ruby_token_operator | @ruby_token_simple_symbol | @ruby_underscore_nonlocal_variable + +@ruby_underscore_nonlocal_variable = @ruby_token_class_variable | @ruby_token_global_variable | @ruby_token_instance_variable + +@ruby_underscore_pattern_constant = @ruby_scope_resolution | @ruby_token_constant + +@ruby_underscore_pattern_expr = @ruby_alternative_pattern | @ruby_as_pattern | @ruby_underscore_pattern_expr_basic + +@ruby_underscore_pattern_expr_basic = @ruby_array_pattern | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_parenthesized_pattern | @ruby_range | @ruby_token_identifier | @ruby_underscore_pattern_constant | @ruby_underscore_pattern_primitive | @ruby_variable_reference_pattern + +@ruby_underscore_pattern_primitive = @ruby_delimited_symbol | @ruby_lambda | @ruby_regex | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_encoding | @ruby_token_false | @ruby_token_file | @ruby_token_heredoc_beginning | @ruby_token_line | @ruby_token_nil | @ruby_token_self | @ruby_token_simple_symbol | @ruby_token_true | @ruby_unary | @ruby_underscore_simple_numeric + +@ruby_underscore_pattern_top_expr_body = @ruby_array_pattern | @ruby_find_pattern | @ruby_hash_pattern | @ruby_underscore_pattern_expr + +@ruby_underscore_primary = @ruby_array | @ruby_begin | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_delimited_symbol | @ruby_for | @ruby_hash | @ruby_if | @ruby_lambda | @ruby_method | @ruby_module | @ruby_next | @ruby_parenthesized_statements | @ruby_redo | @ruby_regex | @ruby_retry | @ruby_return | @ruby_singleton_class | @ruby_singleton_method | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_symbol_array | @ruby_token_character | @ruby_token_heredoc_beginning | @ruby_token_simple_symbol | @ruby_unary | @ruby_underscore_lhs | @ruby_underscore_simple_numeric | @ruby_unless | @ruby_until | @ruby_while | @ruby_yield + +@ruby_underscore_simple_numeric = @ruby_complex | @ruby_rational | @ruby_token_float | @ruby_token_integer + +@ruby_underscore_statement = @ruby_alias | @ruby_begin_block | @ruby_end_block | @ruby_if_modifier | @ruby_rescue_modifier | @ruby_undef | @ruby_underscore_expression | @ruby_unless_modifier | @ruby_until_modifier | @ruby_while_modifier + +@ruby_underscore_variable = @ruby_token_constant | @ruby_token_identifier | @ruby_token_self | @ruby_token_super | @ruby_underscore_nonlocal_variable + +ruby_alias_def( + unique int id: @ruby_alias, + int alias: @ruby_underscore_method_name ref, + int name: @ruby_underscore_method_name ref +); + +#keyset[ruby_alternative_pattern, index] +ruby_alternative_pattern_alternatives( + int ruby_alternative_pattern: @ruby_alternative_pattern ref, + int index: int ref, + unique int alternatives: @ruby_underscore_pattern_expr_basic ref +); + +ruby_alternative_pattern_def( + unique int id: @ruby_alternative_pattern +); + +@ruby_argument_list_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_argument_list, index] +ruby_argument_list_child( + int ruby_argument_list: @ruby_argument_list ref, + int index: int ref, + unique int child: @ruby_argument_list_child_type ref +); + +ruby_argument_list_def( + unique int id: @ruby_argument_list +); + +@ruby_array_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_array, index] +ruby_array_child( + int ruby_array: @ruby_array ref, + int index: int ref, + unique int child: @ruby_array_child_type ref +); + +ruby_array_def( + unique int id: @ruby_array +); + +ruby_array_pattern_class( + unique int ruby_array_pattern: @ruby_array_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_array_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_array_pattern, index] +ruby_array_pattern_child( + int ruby_array_pattern: @ruby_array_pattern ref, + int index: int ref, + unique int child: @ruby_array_pattern_child_type ref +); + +ruby_array_pattern_def( + unique int id: @ruby_array_pattern +); + +ruby_as_pattern_def( + unique int id: @ruby_as_pattern, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_pattern_expr ref +); + +@ruby_assignment_left_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +@ruby_assignment_right_type = @ruby_rescue_modifier | @ruby_right_assignment_list | @ruby_splat_argument | @ruby_underscore_expression + +ruby_assignment_def( + unique int id: @ruby_assignment, + int left: @ruby_assignment_left_type ref, + int right: @ruby_assignment_right_type ref +); + +@ruby_bare_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_string, index] +ruby_bare_string_child( + int ruby_bare_string: @ruby_bare_string ref, + int index: int ref, + unique int child: @ruby_bare_string_child_type ref +); + +ruby_bare_string_def( + unique int id: @ruby_bare_string +); + +@ruby_bare_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_bare_symbol, index] +ruby_bare_symbol_child( + int ruby_bare_symbol: @ruby_bare_symbol ref, + int index: int ref, + unique int child: @ruby_bare_symbol_child_type ref +); + +ruby_bare_symbol_def( + unique int id: @ruby_bare_symbol +); + +@ruby_begin_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin, index] +ruby_begin_child( + int ruby_begin: @ruby_begin ref, + int index: int ref, + unique int child: @ruby_begin_child_type ref +); + +ruby_begin_def( + unique int id: @ruby_begin +); + +@ruby_begin_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_begin_block, index] +ruby_begin_block_child( + int ruby_begin_block: @ruby_begin_block ref, + int index: int ref, + unique int child: @ruby_begin_block_child_type ref +); + +ruby_begin_block_def( + unique int id: @ruby_begin_block +); + +@ruby_binary_left_type = @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_binary.operator of + 0 = @ruby_binary_bangequal +| 1 = @ruby_binary_bangtilde +| 2 = @ruby_binary_percent +| 3 = @ruby_binary_ampersand +| 4 = @ruby_binary_ampersandampersand +| 5 = @ruby_binary_star +| 6 = @ruby_binary_starstar +| 7 = @ruby_binary_plus +| 8 = @ruby_binary_minus +| 9 = @ruby_binary_slash +| 10 = @ruby_binary_langle +| 11 = @ruby_binary_langlelangle +| 12 = @ruby_binary_langleequal +| 13 = @ruby_binary_langleequalrangle +| 14 = @ruby_binary_equalequal +| 15 = @ruby_binary_equalequalequal +| 16 = @ruby_binary_equaltilde +| 17 = @ruby_binary_rangle +| 18 = @ruby_binary_rangleequal +| 19 = @ruby_binary_ranglerangle +| 20 = @ruby_binary_caret +| 21 = @ruby_binary_and +| 22 = @ruby_binary_or +| 23 = @ruby_binary_pipe +| 24 = @ruby_binary_pipepipe +; + + +ruby_binary_def( + unique int id: @ruby_binary, + int left: @ruby_binary_left_type ref, + int operator: int ref, + int right: @ruby_underscore_expression ref +); + +ruby_block_body( + unique int ruby_block: @ruby_block ref, + unique int body: @ruby_block_body ref +); + +ruby_block_parameters( + unique int ruby_block: @ruby_block ref, + unique int parameters: @ruby_block_parameters ref +); + +ruby_block_def( + unique int id: @ruby_block +); + +ruby_block_argument_child( + unique int ruby_block_argument: @ruby_block_argument ref, + unique int child: @ruby_underscore_arg ref +); + +ruby_block_argument_def( + unique int id: @ruby_block_argument +); + +@ruby_block_body_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_block_body, index] +ruby_block_body_child( + int ruby_block_body: @ruby_block_body ref, + int index: int ref, + unique int child: @ruby_block_body_child_type ref +); + +ruby_block_body_def( + unique int id: @ruby_block_body +); + +ruby_block_parameter_name( + unique int ruby_block_parameter: @ruby_block_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_block_parameter_def( + unique int id: @ruby_block_parameter +); + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_locals( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int locals: @ruby_token_identifier ref +); + +@ruby_block_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_block_parameters, index] +ruby_block_parameters_child( + int ruby_block_parameters: @ruby_block_parameters ref, + int index: int ref, + unique int child: @ruby_block_parameters_child_type ref +); + +ruby_block_parameters_def( + unique int id: @ruby_block_parameters +); + +@ruby_body_statement_child_type = @ruby_else | @ruby_ensure | @ruby_rescue | @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_body_statement, index] +ruby_body_statement_child( + int ruby_body_statement: @ruby_body_statement ref, + int index: int ref, + unique int child: @ruby_body_statement_child_type ref +); + +ruby_body_statement_def( + unique int id: @ruby_body_statement +); + +ruby_break_child( + unique int ruby_break: @ruby_break ref, + unique int child: @ruby_argument_list ref +); + +ruby_break_def( + unique int id: @ruby_break +); + +ruby_call_arguments( + unique int ruby_call: @ruby_call ref, + unique int arguments: @ruby_argument_list ref +); + +@ruby_call_block_type = @ruby_block | @ruby_do_block + +ruby_call_block( + unique int ruby_call: @ruby_call ref, + unique int block: @ruby_call_block_type ref +); + +@ruby_call_method_type = @ruby_token_operator | @ruby_underscore_variable + +ruby_call_method( + unique int ruby_call: @ruby_call ref, + unique int method: @ruby_call_method_type ref +); + +ruby_call_operator( + unique int ruby_call: @ruby_call ref, + unique int operator: @ruby_underscore_call_operator ref +); + +ruby_call_receiver( + unique int ruby_call: @ruby_call ref, + unique int receiver: @ruby_underscore_primary ref +); + +ruby_call_def( + unique int id: @ruby_call +); + +ruby_case_value( + unique int ruby_case__: @ruby_case__ ref, + unique int value: @ruby_underscore_statement ref +); + +@ruby_case_child_type = @ruby_else | @ruby_when + +#keyset[ruby_case__, index] +ruby_case_child( + int ruby_case__: @ruby_case__ ref, + int index: int ref, + unique int child: @ruby_case_child_type ref +); + +ruby_case_def( + unique int id: @ruby_case__ +); + +#keyset[ruby_case_match, index] +ruby_case_match_clauses( + int ruby_case_match: @ruby_case_match ref, + int index: int ref, + unique int clauses: @ruby_in_clause ref +); + +ruby_case_match_else( + unique int ruby_case_match: @ruby_case_match ref, + unique int else: @ruby_else ref +); + +ruby_case_match_def( + unique int id: @ruby_case_match, + int value: @ruby_underscore_statement ref +); + +#keyset[ruby_chained_string, index] +ruby_chained_string_child( + int ruby_chained_string: @ruby_chained_string ref, + int index: int ref, + unique int child: @ruby_string__ ref +); + +ruby_chained_string_def( + unique int id: @ruby_chained_string +); + +ruby_class_body( + unique int ruby_class: @ruby_class ref, + unique int body: @ruby_body_statement ref +); + +@ruby_class_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_class_superclass( + unique int ruby_class: @ruby_class ref, + unique int superclass: @ruby_superclass ref +); + +ruby_class_def( + unique int id: @ruby_class, + int name: @ruby_class_name_type ref +); + +@ruby_complex_child_type = @ruby_rational | @ruby_token_float | @ruby_token_integer + +ruby_complex_def( + unique int id: @ruby_complex, + int child: @ruby_complex_child_type ref +); + +ruby_conditional_def( + unique int id: @ruby_conditional, + int alternative: @ruby_underscore_arg ref, + int condition: @ruby_underscore_arg ref, + int consequence: @ruby_underscore_arg ref +); + +@ruby_delimited_symbol_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_delimited_symbol, index] +ruby_delimited_symbol_child( + int ruby_delimited_symbol: @ruby_delimited_symbol ref, + int index: int ref, + unique int child: @ruby_delimited_symbol_child_type ref +); + +ruby_delimited_symbol_def( + unique int id: @ruby_delimited_symbol +); + +@ruby_destructured_left_assignment_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_destructured_left_assignment, index] +ruby_destructured_left_assignment_child( + int ruby_destructured_left_assignment: @ruby_destructured_left_assignment ref, + int index: int ref, + unique int child: @ruby_destructured_left_assignment_child_type ref +); + +ruby_destructured_left_assignment_def( + unique int id: @ruby_destructured_left_assignment +); + +@ruby_destructured_parameter_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_destructured_parameter, index] +ruby_destructured_parameter_child( + int ruby_destructured_parameter: @ruby_destructured_parameter ref, + int index: int ref, + unique int child: @ruby_destructured_parameter_child_type ref +); + +ruby_destructured_parameter_def( + unique int id: @ruby_destructured_parameter +); + +@ruby_do_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_do, index] +ruby_do_child( + int ruby_do: @ruby_do ref, + int index: int ref, + unique int child: @ruby_do_child_type ref +); + +ruby_do_def( + unique int id: @ruby_do +); + +ruby_do_block_body( + unique int ruby_do_block: @ruby_do_block ref, + unique int body: @ruby_body_statement ref +); + +ruby_do_block_parameters( + unique int ruby_do_block: @ruby_do_block ref, + unique int parameters: @ruby_block_parameters ref +); + +ruby_do_block_def( + unique int id: @ruby_do_block +); + +@ruby_element_reference_child_type = @ruby_block_argument | @ruby_hash_splat_argument | @ruby_pair | @ruby_splat_argument | @ruby_token_forward_argument | @ruby_underscore_expression + +#keyset[ruby_element_reference, index] +ruby_element_reference_child( + int ruby_element_reference: @ruby_element_reference ref, + int index: int ref, + unique int child: @ruby_element_reference_child_type ref +); + +ruby_element_reference_def( + unique int id: @ruby_element_reference, + int object: @ruby_underscore_primary ref +); + +@ruby_else_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_else, index] +ruby_else_child( + int ruby_else: @ruby_else ref, + int index: int ref, + unique int child: @ruby_else_child_type ref +); + +ruby_else_def( + unique int id: @ruby_else +); + +@ruby_elsif_alternative_type = @ruby_else | @ruby_elsif + +ruby_elsif_alternative( + unique int ruby_elsif: @ruby_elsif ref, + unique int alternative: @ruby_elsif_alternative_type ref +); + +ruby_elsif_consequence( + unique int ruby_elsif: @ruby_elsif ref, + unique int consequence: @ruby_then ref +); + +ruby_elsif_def( + unique int id: @ruby_elsif, + int condition: @ruby_underscore_statement ref +); + +@ruby_end_block_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_end_block, index] +ruby_end_block_child( + int ruby_end_block: @ruby_end_block ref, + int index: int ref, + unique int child: @ruby_end_block_child_type ref +); + +ruby_end_block_def( + unique int id: @ruby_end_block +); + +@ruby_ensure_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_ensure, index] +ruby_ensure_child( + int ruby_ensure: @ruby_ensure ref, + int index: int ref, + unique int child: @ruby_ensure_child_type ref +); + +ruby_ensure_def( + unique int id: @ruby_ensure +); + +ruby_exception_variable_def( + unique int id: @ruby_exception_variable, + int child: @ruby_underscore_lhs ref +); + +@ruby_exceptions_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_exceptions, index] +ruby_exceptions_child( + int ruby_exceptions: @ruby_exceptions ref, + int index: int ref, + unique int child: @ruby_exceptions_child_type ref +); + +ruby_exceptions_def( + unique int id: @ruby_exceptions +); + +ruby_expression_reference_pattern_def( + unique int id: @ruby_expression_reference_pattern, + int value: @ruby_underscore_expression ref +); + +ruby_find_pattern_class( + unique int ruby_find_pattern: @ruby_find_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_find_pattern_child_type = @ruby_splat_parameter | @ruby_underscore_pattern_expr + +#keyset[ruby_find_pattern, index] +ruby_find_pattern_child( + int ruby_find_pattern: @ruby_find_pattern ref, + int index: int ref, + unique int child: @ruby_find_pattern_child_type ref +); + +ruby_find_pattern_def( + unique int id: @ruby_find_pattern +); + +@ruby_for_pattern_type = @ruby_left_assignment_list | @ruby_underscore_lhs + +ruby_for_def( + unique int id: @ruby_for, + int body: @ruby_do ref, + int pattern: @ruby_for_pattern_type ref, + int value: @ruby_in ref +); + +@ruby_hash_child_type = @ruby_hash_splat_argument | @ruby_pair + +#keyset[ruby_hash, index] +ruby_hash_child( + int ruby_hash: @ruby_hash ref, + int index: int ref, + unique int child: @ruby_hash_child_type ref +); + +ruby_hash_def( + unique int id: @ruby_hash +); + +ruby_hash_pattern_class( + unique int ruby_hash_pattern: @ruby_hash_pattern ref, + unique int class: @ruby_underscore_pattern_constant ref +); + +@ruby_hash_pattern_child_type = @ruby_hash_splat_parameter | @ruby_keyword_pattern | @ruby_token_hash_splat_nil + +#keyset[ruby_hash_pattern, index] +ruby_hash_pattern_child( + int ruby_hash_pattern: @ruby_hash_pattern ref, + int index: int ref, + unique int child: @ruby_hash_pattern_child_type ref +); + +ruby_hash_pattern_def( + unique int id: @ruby_hash_pattern +); + +ruby_hash_splat_argument_def( + unique int id: @ruby_hash_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_hash_splat_parameter_name( + unique int ruby_hash_splat_parameter: @ruby_hash_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_hash_splat_parameter_def( + unique int id: @ruby_hash_splat_parameter +); + +@ruby_heredoc_body_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_heredoc_content | @ruby_token_heredoc_end + +#keyset[ruby_heredoc_body, index] +ruby_heredoc_body_child( + int ruby_heredoc_body: @ruby_heredoc_body ref, + int index: int ref, + unique int child: @ruby_heredoc_body_child_type ref +); + +ruby_heredoc_body_def( + unique int id: @ruby_heredoc_body +); + +@ruby_if_alternative_type = @ruby_else | @ruby_elsif + +ruby_if_alternative( + unique int ruby_if: @ruby_if ref, + unique int alternative: @ruby_if_alternative_type ref +); + +ruby_if_consequence( + unique int ruby_if: @ruby_if ref, + unique int consequence: @ruby_then ref +); + +ruby_if_def( + unique int id: @ruby_if, + int condition: @ruby_underscore_statement ref +); + +ruby_if_guard_def( + unique int id: @ruby_if_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_if_modifier_def( + unique int id: @ruby_if_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_in_def( + unique int id: @ruby_in, + int child: @ruby_underscore_arg ref +); + +ruby_in_clause_body( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int body: @ruby_then ref +); + +@ruby_in_clause_guard_type = @ruby_if_guard | @ruby_unless_guard + +ruby_in_clause_guard( + unique int ruby_in_clause: @ruby_in_clause ref, + unique int guard: @ruby_in_clause_guard_type ref +); + +ruby_in_clause_def( + unique int id: @ruby_in_clause, + int pattern: @ruby_underscore_pattern_top_expr_body ref +); + +@ruby_interpolation_child_type = @ruby_token_empty_statement | @ruby_underscore_nonlocal_variable | @ruby_underscore_statement + +#keyset[ruby_interpolation, index] +ruby_interpolation_child( + int ruby_interpolation: @ruby_interpolation ref, + int index: int ref, + unique int child: @ruby_interpolation_child_type ref +); + +ruby_interpolation_def( + unique int id: @ruby_interpolation +); + +ruby_keyword_parameter_value( + unique int ruby_keyword_parameter: @ruby_keyword_parameter ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_keyword_parameter_def( + unique int id: @ruby_keyword_parameter, + int name: @ruby_token_identifier ref +); + +@ruby_keyword_pattern_key_type = @ruby_string__ | @ruby_token_hash_key_symbol + +ruby_keyword_pattern_value( + unique int ruby_keyword_pattern: @ruby_keyword_pattern ref, + unique int value: @ruby_underscore_pattern_expr ref +); + +ruby_keyword_pattern_def( + unique int id: @ruby_keyword_pattern, + int key__: @ruby_keyword_pattern_key_type ref +); + +@ruby_lambda_body_type = @ruby_block | @ruby_do_block + +ruby_lambda_parameters( + unique int ruby_lambda: @ruby_lambda ref, + unique int parameters: @ruby_lambda_parameters ref +); + +ruby_lambda_def( + unique int id: @ruby_lambda, + int body: @ruby_lambda_body_type ref +); + +@ruby_lambda_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_lambda_parameters, index] +ruby_lambda_parameters_child( + int ruby_lambda_parameters: @ruby_lambda_parameters ref, + int index: int ref, + unique int child: @ruby_lambda_parameters_child_type ref +); + +ruby_lambda_parameters_def( + unique int id: @ruby_lambda_parameters +); + +@ruby_left_assignment_list_child_type = @ruby_destructured_left_assignment | @ruby_rest_assignment | @ruby_underscore_lhs + +#keyset[ruby_left_assignment_list, index] +ruby_left_assignment_list_child( + int ruby_left_assignment_list: @ruby_left_assignment_list ref, + int index: int ref, + unique int child: @ruby_left_assignment_list_child_type ref +); + +ruby_left_assignment_list_def( + unique int id: @ruby_left_assignment_list +); + +@ruby_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_method_body( + unique int ruby_method: @ruby_method ref, + unique int body: @ruby_method_body_type ref +); + +ruby_method_parameters( + unique int ruby_method: @ruby_method ref, + unique int parameters: @ruby_method_parameters ref +); + +ruby_method_def( + unique int id: @ruby_method, + int name: @ruby_underscore_method_name ref +); + +@ruby_method_parameters_child_type = @ruby_block_parameter | @ruby_destructured_parameter | @ruby_hash_splat_parameter | @ruby_keyword_parameter | @ruby_optional_parameter | @ruby_splat_parameter | @ruby_token_forward_parameter | @ruby_token_hash_splat_nil | @ruby_token_identifier + +#keyset[ruby_method_parameters, index] +ruby_method_parameters_child( + int ruby_method_parameters: @ruby_method_parameters ref, + int index: int ref, + unique int child: @ruby_method_parameters_child_type ref +); + +ruby_method_parameters_def( + unique int id: @ruby_method_parameters +); + +ruby_module_body( + unique int ruby_module: @ruby_module ref, + unique int body: @ruby_body_statement ref +); + +@ruby_module_name_type = @ruby_scope_resolution | @ruby_token_constant + +ruby_module_def( + unique int id: @ruby_module, + int name: @ruby_module_name_type ref +); + +ruby_next_child( + unique int ruby_next: @ruby_next ref, + unique int child: @ruby_argument_list ref +); + +ruby_next_def( + unique int id: @ruby_next +); + +case @ruby_operator_assignment.operator of + 0 = @ruby_operator_assignment_percentequal +| 1 = @ruby_operator_assignment_ampersandampersandequal +| 2 = @ruby_operator_assignment_ampersandequal +| 3 = @ruby_operator_assignment_starstarequal +| 4 = @ruby_operator_assignment_starequal +| 5 = @ruby_operator_assignment_plusequal +| 6 = @ruby_operator_assignment_minusequal +| 7 = @ruby_operator_assignment_slashequal +| 8 = @ruby_operator_assignment_langlelangleequal +| 9 = @ruby_operator_assignment_ranglerangleequal +| 10 = @ruby_operator_assignment_caretequal +| 11 = @ruby_operator_assignment_pipeequal +| 12 = @ruby_operator_assignment_pipepipeequal +; + + +@ruby_operator_assignment_right_type = @ruby_rescue_modifier | @ruby_underscore_expression + +ruby_operator_assignment_def( + unique int id: @ruby_operator_assignment, + int left: @ruby_underscore_lhs ref, + int operator: int ref, + int right: @ruby_operator_assignment_right_type ref +); + +ruby_optional_parameter_def( + unique int id: @ruby_optional_parameter, + int name: @ruby_token_identifier ref, + int value: @ruby_underscore_arg ref +); + +@ruby_pair_key_type = @ruby_string__ | @ruby_token_hash_key_symbol | @ruby_underscore_arg + +ruby_pair_value( + unique int ruby_pair: @ruby_pair ref, + unique int value: @ruby_underscore_arg ref +); + +ruby_pair_def( + unique int id: @ruby_pair, + int key__: @ruby_pair_key_type ref +); + +ruby_parenthesized_pattern_def( + unique int id: @ruby_parenthesized_pattern, + int child: @ruby_underscore_pattern_expr ref +); + +@ruby_parenthesized_statements_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_parenthesized_statements, index] +ruby_parenthesized_statements_child( + int ruby_parenthesized_statements: @ruby_parenthesized_statements ref, + int index: int ref, + unique int child: @ruby_parenthesized_statements_child_type ref +); + +ruby_parenthesized_statements_def( + unique int id: @ruby_parenthesized_statements +); + +@ruby_pattern_child_type = @ruby_splat_argument | @ruby_underscore_arg + +ruby_pattern_def( + unique int id: @ruby_pattern, + int child: @ruby_pattern_child_type ref +); + +@ruby_program_child_type = @ruby_token_empty_statement | @ruby_token_uninterpreted | @ruby_underscore_statement + +#keyset[ruby_program, index] +ruby_program_child( + int ruby_program: @ruby_program ref, + int index: int ref, + unique int child: @ruby_program_child_type ref +); + +ruby_program_def( + unique int id: @ruby_program +); + +@ruby_range_begin_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_begin( + unique int ruby_range: @ruby_range ref, + unique int begin: @ruby_range_begin_type ref +); + +@ruby_range_end_type = @ruby_underscore_arg | @ruby_underscore_pattern_primitive + +ruby_range_end( + unique int ruby_range: @ruby_range ref, + unique int end: @ruby_range_end_type ref +); + +case @ruby_range.operator of + 0 = @ruby_range_dotdot +| 1 = @ruby_range_dotdotdot +; + + +ruby_range_def( + unique int id: @ruby_range, + int operator: int ref +); + +@ruby_rational_child_type = @ruby_token_float | @ruby_token_integer + +ruby_rational_def( + unique int id: @ruby_rational, + int child: @ruby_rational_child_type ref +); + +ruby_redo_child( + unique int ruby_redo: @ruby_redo ref, + unique int child: @ruby_argument_list ref +); + +ruby_redo_def( + unique int id: @ruby_redo +); + +@ruby_regex_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_regex, index] +ruby_regex_child( + int ruby_regex: @ruby_regex ref, + int index: int ref, + unique int child: @ruby_regex_child_type ref +); + +ruby_regex_def( + unique int id: @ruby_regex +); + +ruby_rescue_body( + unique int ruby_rescue: @ruby_rescue ref, + unique int body: @ruby_then ref +); + +ruby_rescue_exceptions( + unique int ruby_rescue: @ruby_rescue ref, + unique int exceptions: @ruby_exceptions ref +); + +ruby_rescue_variable( + unique int ruby_rescue: @ruby_rescue ref, + unique int variable: @ruby_exception_variable ref +); + +ruby_rescue_def( + unique int id: @ruby_rescue +); + +@ruby_rescue_modifier_body_type = @ruby_underscore_arg | @ruby_underscore_statement + +ruby_rescue_modifier_def( + unique int id: @ruby_rescue_modifier, + int body: @ruby_rescue_modifier_body_type ref, + int handler: @ruby_underscore_expression ref +); + +ruby_rest_assignment_child( + unique int ruby_rest_assignment: @ruby_rest_assignment ref, + unique int child: @ruby_underscore_lhs ref +); + +ruby_rest_assignment_def( + unique int id: @ruby_rest_assignment +); + +ruby_retry_child( + unique int ruby_retry: @ruby_retry ref, + unique int child: @ruby_argument_list ref +); + +ruby_retry_def( + unique int id: @ruby_retry +); + +ruby_return_child( + unique int ruby_return: @ruby_return ref, + unique int child: @ruby_argument_list ref +); + +ruby_return_def( + unique int id: @ruby_return +); + +@ruby_right_assignment_list_child_type = @ruby_splat_argument | @ruby_underscore_arg + +#keyset[ruby_right_assignment_list, index] +ruby_right_assignment_list_child( + int ruby_right_assignment_list: @ruby_right_assignment_list ref, + int index: int ref, + unique int child: @ruby_right_assignment_list_child_type ref +); + +ruby_right_assignment_list_def( + unique int id: @ruby_right_assignment_list +); + +@ruby_scope_resolution_scope_type = @ruby_underscore_pattern_constant | @ruby_underscore_primary + +ruby_scope_resolution_scope( + unique int ruby_scope_resolution: @ruby_scope_resolution ref, + unique int scope: @ruby_scope_resolution_scope_type ref +); + +ruby_scope_resolution_def( + unique int id: @ruby_scope_resolution, + int name: @ruby_token_constant ref +); + +ruby_setter_def( + unique int id: @ruby_setter, + int name: @ruby_token_identifier ref +); + +ruby_singleton_class_body( + unique int ruby_singleton_class: @ruby_singleton_class ref, + unique int body: @ruby_body_statement ref +); + +ruby_singleton_class_def( + unique int id: @ruby_singleton_class, + int value: @ruby_underscore_arg ref +); + +@ruby_singleton_method_body_type = @ruby_body_statement | @ruby_rescue_modifier | @ruby_underscore_arg + +ruby_singleton_method_body( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int body: @ruby_singleton_method_body_type ref +); + +@ruby_singleton_method_object_type = @ruby_underscore_arg | @ruby_underscore_variable + +ruby_singleton_method_parameters( + unique int ruby_singleton_method: @ruby_singleton_method ref, + unique int parameters: @ruby_method_parameters ref +); + +ruby_singleton_method_def( + unique int id: @ruby_singleton_method, + int name: @ruby_underscore_method_name ref, + int object: @ruby_singleton_method_object_type ref +); + +ruby_splat_argument_def( + unique int id: @ruby_splat_argument, + int child: @ruby_underscore_arg ref +); + +ruby_splat_parameter_name( + unique int ruby_splat_parameter: @ruby_splat_parameter ref, + unique int name: @ruby_token_identifier ref +); + +ruby_splat_parameter_def( + unique int id: @ruby_splat_parameter +); + +@ruby_string_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_string__, index] +ruby_string_child( + int ruby_string__: @ruby_string__ ref, + int index: int ref, + unique int child: @ruby_string_child_type ref +); + +ruby_string_def( + unique int id: @ruby_string__ +); + +#keyset[ruby_string_array, index] +ruby_string_array_child( + int ruby_string_array: @ruby_string_array ref, + int index: int ref, + unique int child: @ruby_bare_string ref +); + +ruby_string_array_def( + unique int id: @ruby_string_array +); + +@ruby_subshell_child_type = @ruby_interpolation | @ruby_token_escape_sequence | @ruby_token_string_content + +#keyset[ruby_subshell, index] +ruby_subshell_child( + int ruby_subshell: @ruby_subshell ref, + int index: int ref, + unique int child: @ruby_subshell_child_type ref +); + +ruby_subshell_def( + unique int id: @ruby_subshell +); + +ruby_superclass_def( + unique int id: @ruby_superclass, + int child: @ruby_underscore_expression ref +); + +#keyset[ruby_symbol_array, index] +ruby_symbol_array_child( + int ruby_symbol_array: @ruby_symbol_array ref, + int index: int ref, + unique int child: @ruby_bare_symbol ref +); + +ruby_symbol_array_def( + unique int id: @ruby_symbol_array +); + +@ruby_then_child_type = @ruby_token_empty_statement | @ruby_underscore_statement + +#keyset[ruby_then, index] +ruby_then_child( + int ruby_then: @ruby_then ref, + int index: int ref, + unique int child: @ruby_then_child_type ref +); + +ruby_then_def( + unique int id: @ruby_then +); + +@ruby_unary_operand_type = @ruby_parenthesized_statements | @ruby_underscore_expression | @ruby_underscore_simple_numeric + +case @ruby_unary.operator of + 0 = @ruby_unary_bang +| 1 = @ruby_unary_plus +| 2 = @ruby_unary_minus +| 3 = @ruby_unary_definedquestion +| 4 = @ruby_unary_not +| 5 = @ruby_unary_tilde +; + + +ruby_unary_def( + unique int id: @ruby_unary, + int operand: @ruby_unary_operand_type ref, + int operator: int ref +); + +#keyset[ruby_undef, index] +ruby_undef_child( + int ruby_undef: @ruby_undef ref, + int index: int ref, + unique int child: @ruby_underscore_method_name ref +); + +ruby_undef_def( + unique int id: @ruby_undef +); + +@ruby_unless_alternative_type = @ruby_else | @ruby_elsif + +ruby_unless_alternative( + unique int ruby_unless: @ruby_unless ref, + unique int alternative: @ruby_unless_alternative_type ref +); + +ruby_unless_consequence( + unique int ruby_unless: @ruby_unless ref, + unique int consequence: @ruby_then ref +); + +ruby_unless_def( + unique int id: @ruby_unless, + int condition: @ruby_underscore_statement ref +); + +ruby_unless_guard_def( + unique int id: @ruby_unless_guard, + int condition: @ruby_underscore_expression ref +); + +ruby_unless_modifier_def( + unique int id: @ruby_unless_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_until_def( + unique int id: @ruby_until, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_until_modifier_def( + unique int id: @ruby_until_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +@ruby_variable_reference_pattern_name_type = @ruby_token_identifier | @ruby_underscore_nonlocal_variable + +ruby_variable_reference_pattern_def( + unique int id: @ruby_variable_reference_pattern, + int name: @ruby_variable_reference_pattern_name_type ref +); + +ruby_when_body( + unique int ruby_when: @ruby_when ref, + unique int body: @ruby_then ref +); + +#keyset[ruby_when, index] +ruby_when_pattern( + int ruby_when: @ruby_when ref, + int index: int ref, + unique int pattern: @ruby_pattern ref +); + +ruby_when_def( + unique int id: @ruby_when +); + +ruby_while_def( + unique int id: @ruby_while, + int body: @ruby_do ref, + int condition: @ruby_underscore_statement ref +); + +ruby_while_modifier_def( + unique int id: @ruby_while_modifier, + int body: @ruby_underscore_statement ref, + int condition: @ruby_underscore_expression ref +); + +ruby_yield_child( + unique int ruby_yield: @ruby_yield ref, + unique int child: @ruby_argument_list ref +); + +ruby_yield_def( + unique int id: @ruby_yield +); + +ruby_tokeninfo( + unique int id: @ruby_token, + int kind: int ref, + string value: string ref +); + +case @ruby_token.kind of + 0 = @ruby_reserved_word +| 1 = @ruby_token_character +| 2 = @ruby_token_class_variable +| 3 = @ruby_token_comment +| 4 = @ruby_token_constant +| 5 = @ruby_token_empty_statement +| 6 = @ruby_token_encoding +| 7 = @ruby_token_escape_sequence +| 8 = @ruby_token_false +| 9 = @ruby_token_file +| 10 = @ruby_token_float +| 11 = @ruby_token_forward_argument +| 12 = @ruby_token_forward_parameter +| 13 = @ruby_token_global_variable +| 14 = @ruby_token_hash_key_symbol +| 15 = @ruby_token_hash_splat_nil +| 16 = @ruby_token_heredoc_beginning +| 17 = @ruby_token_heredoc_content +| 18 = @ruby_token_heredoc_end +| 19 = @ruby_token_identifier +| 20 = @ruby_token_instance_variable +| 21 = @ruby_token_integer +| 22 = @ruby_token_line +| 23 = @ruby_token_nil +| 24 = @ruby_token_operator +| 25 = @ruby_token_self +| 26 = @ruby_token_simple_symbol +| 27 = @ruby_token_string_content +| 28 = @ruby_token_super +| 29 = @ruby_token_true +| 30 = @ruby_token_uninterpreted +; + + +@ruby_ast_node = @ruby_alias | @ruby_alternative_pattern | @ruby_argument_list | @ruby_array | @ruby_array_pattern | @ruby_as_pattern | @ruby_assignment | @ruby_bare_string | @ruby_bare_symbol | @ruby_begin | @ruby_begin_block | @ruby_binary | @ruby_block | @ruby_block_argument | @ruby_block_body | @ruby_block_parameter | @ruby_block_parameters | @ruby_body_statement | @ruby_break | @ruby_call | @ruby_case__ | @ruby_case_match | @ruby_chained_string | @ruby_class | @ruby_complex | @ruby_conditional | @ruby_delimited_symbol | @ruby_destructured_left_assignment | @ruby_destructured_parameter | @ruby_do | @ruby_do_block | @ruby_element_reference | @ruby_else | @ruby_elsif | @ruby_end_block | @ruby_ensure | @ruby_exception_variable | @ruby_exceptions | @ruby_expression_reference_pattern | @ruby_find_pattern | @ruby_for | @ruby_hash | @ruby_hash_pattern | @ruby_hash_splat_argument | @ruby_hash_splat_parameter | @ruby_heredoc_body | @ruby_if | @ruby_if_guard | @ruby_if_modifier | @ruby_in | @ruby_in_clause | @ruby_interpolation | @ruby_keyword_parameter | @ruby_keyword_pattern | @ruby_lambda | @ruby_lambda_parameters | @ruby_left_assignment_list | @ruby_method | @ruby_method_parameters | @ruby_module | @ruby_next | @ruby_operator_assignment | @ruby_optional_parameter | @ruby_pair | @ruby_parenthesized_pattern | @ruby_parenthesized_statements | @ruby_pattern | @ruby_program | @ruby_range | @ruby_rational | @ruby_redo | @ruby_regex | @ruby_rescue | @ruby_rescue_modifier | @ruby_rest_assignment | @ruby_retry | @ruby_return | @ruby_right_assignment_list | @ruby_scope_resolution | @ruby_setter | @ruby_singleton_class | @ruby_singleton_method | @ruby_splat_argument | @ruby_splat_parameter | @ruby_string__ | @ruby_string_array | @ruby_subshell | @ruby_superclass | @ruby_symbol_array | @ruby_then | @ruby_token | @ruby_unary | @ruby_undef | @ruby_unless | @ruby_unless_guard | @ruby_unless_modifier | @ruby_until | @ruby_until_modifier | @ruby_variable_reference_pattern | @ruby_when | @ruby_while | @ruby_while_modifier | @ruby_yield + +@ruby_ast_node_parent = @file | @ruby_ast_node + +#keyset[parent, parent_index] +ruby_ast_node_info( + unique int node: @ruby_ast_node ref, + int parent: @ruby_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + +erb_comment_directive_child( + unique int erb_comment_directive: @erb_comment_directive ref, + unique int child: @erb_token_comment ref +); + +erb_comment_directive_def( + unique int id: @erb_comment_directive +); + +erb_directive_child( + unique int erb_directive: @erb_directive ref, + unique int child: @erb_token_code ref +); + +erb_directive_def( + unique int id: @erb_directive +); + +erb_graphql_directive_child( + unique int erb_graphql_directive: @erb_graphql_directive ref, + unique int child: @erb_token_code ref +); + +erb_graphql_directive_def( + unique int id: @erb_graphql_directive +); + +erb_output_directive_child( + unique int erb_output_directive: @erb_output_directive ref, + unique int child: @erb_token_code ref +); + +erb_output_directive_def( + unique int id: @erb_output_directive +); + +@erb_template_child_type = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_token_content + +#keyset[erb_template, index] +erb_template_child( + int erb_template: @erb_template ref, + int index: int ref, + unique int child: @erb_template_child_type ref +); + +erb_template_def( + unique int id: @erb_template +); + +erb_tokeninfo( + unique int id: @erb_token, + int kind: int ref, + string value: string ref +); + +case @erb_token.kind of + 0 = @erb_reserved_word +| 1 = @erb_token_code +| 2 = @erb_token_comment +| 3 = @erb_token_content +; + + +@erb_ast_node = @erb_comment_directive | @erb_directive | @erb_graphql_directive | @erb_output_directive | @erb_template | @erb_token + +@erb_ast_node_parent = @erb_ast_node | @file + +#keyset[parent, parent_index] +erb_ast_node_info( + unique int node: @erb_ast_node ref, + int parent: @erb_ast_node_parent ref, + int parent_index: int ref, + int loc: @location ref +); + diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_ast_node_info.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_ast_node_info.ql new file mode 100644 index 00000000000..abc6f7bf144 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_ast_node_info.ql @@ -0,0 +1,76 @@ +class AstNode extends @ruby_ast_node_parent { + string toString() { none() } +} + +class Location extends @location { + string toString() { none() } +} + +private predicate body_statement(AstNode body, AstNode firstChild) { + exists(AstNode node | + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, 0, firstChild) + or + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, 0, firstChild) + or + ruby_method_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, 0, firstChild) + or + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, 0, firstChild) + or + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, 0, firstChild) + or + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, 0, firstChild) + or + ruby_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(node, 0, firstChild) + ) +} + +private predicate body_statement_child(AstNode body, int index, AstNode child) { + exists(AstNode parent, AstNode firstChild, int firstChildIndex | + body_statement(body, firstChild) and + ruby_ast_node_info(firstChild, parent, firstChildIndex, _) and + child = + rank[index + 1](AstNode c, int i | + ruby_ast_node_info(c, parent, i, _) and i >= firstChildIndex and c != body + | + c order by i + ) + ) +} + +private predicate astNodeInfo(AstNode node, AstNode parent, int parent_index, Location loc) { + ruby_ast_node_info(node, parent, parent_index, loc) and + not body_statement(node, _) and + not body_statement_child(_, _, node) +} + +from AstNode node, AstNode parent, int parent_index, Location loc +where + astNodeInfo(node, parent, parent_index, loc) + or + body_statement_child(parent, parent_index, node) and ruby_ast_node_info(node, _, _, loc) + or + body_statement(node, _) and + ruby_ast_node_info(node, parent, _, loc) and + parent_index = count(AstNode n | astNodeInfo(n, parent, _, _)) +select node, parent, parent_index, loc diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body.ql new file mode 100644 index 00000000000..51d629755a8 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_block, AstNode body +where + ruby_block_def(ruby_block) and + ruby_ast_node_info(body, ruby_block, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(ruby_block, _, _) +select ruby_block, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_child.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_child.ql new file mode 100644 index 00000000000..d0743a7a340 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_child.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode ruby_block, AstNode body, int index, AstNode child +where + ruby_block_def(ruby_block) and + ruby_ast_node_info(body, ruby_block, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(ruby_block, index, child) +select body, index, child diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_def.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_def.ql new file mode 100644 index 00000000000..78204867e6f --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_block_body_def.ql @@ -0,0 +1,18 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +/* + * It's not possible to generate fresh IDs for the new ruby_block_body nodes, + * therefore we re-purpose the "}"-token that closes the block and use its ID instead. + * As a result the AST will be missing the "}" tokens, but those are unlikely to be used + * for anything. + */ + +from AstNode ruby_block, AstNode body +where + ruby_block_def(ruby_block) and + ruby_ast_node_info(body, ruby_block, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(ruby_block, _, _) +select body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_child.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_child.ql new file mode 100644 index 00000000000..682c124cb49 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_child.ql @@ -0,0 +1,36 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body, int index, AstNode child +where + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, index, child) + or + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, index, child) + or + ruby_method_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, index, child) + or + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, index, child) + or + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, index, child) + or + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, index, child) +select body, index, child diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_def.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_def.ql new file mode 100644 index 00000000000..603b5d13215 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_body_statement_def.ql @@ -0,0 +1,43 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +/* + * It's not possible to generate fresh IDs for the new ruby_body_statement nodes, + * therefore we re-purpose the "end"-token that closes the block and use its ID instead. + * As a result the AST will be missing the "end" tokens, but those are unlikely to be used + * for anything. + */ + +from AstNode node, AstNode body +where + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, _, _) + or + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, _, _) + or + ruby_method_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, _, _) + or + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, _, _) + or + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, _, _) + or + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, _, _) +select body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_class_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_class_body.ql new file mode 100644 index 00000000000..0a5a7acbc4d --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_class_body.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, _, _) +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_do_block_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_do_block_body.ql new file mode 100644 index 00000000000..dee41a91532 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_do_block_body.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, _, _) +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_method_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_method_body.ql new file mode 100644 index 00000000000..88889a382a4 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_method_body.ql @@ -0,0 +1,20 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_method_def(node, _) and + ( + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, _, _) + or + ruby_method_child(node, 0, body) and + not exists(AstNode n | + ruby_ast_node_info(n, node, _, _) and + ruby_tokeninfo(n, _, "end") + ) + ) +// TODO : handle end-less methods +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_module_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_module_body.ql new file mode 100644 index 00000000000..514dffc45fb --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_module_body.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, _, _) +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_class_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_class_body.ql new file mode 100644 index 00000000000..db90c1ded79 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_class_body.ql @@ -0,0 +1,11 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, _, _) +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_method_body.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_method_body.ql new file mode 100644 index 00000000000..9d714fad0b8 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_singleton_method_body.ql @@ -0,0 +1,17 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +from AstNode node, AstNode body +where + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, _, _) + or + ruby_singleton_method_child(node, 0, body) and + not exists(AstNode n | + ruby_ast_node_info(n, node, _, _) and + ruby_tokeninfo(n, _, "end") + ) +select node, body diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_tokeninfo.ql b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_tokeninfo.ql new file mode 100644 index 00000000000..358445c4204 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/ruby_tokeninfo.ql @@ -0,0 +1,46 @@ +class AstNode extends @ruby_ast_node { + string toString() { none() } +} + +private predicate body_statement(AstNode body) { + exists(AstNode node | + ruby_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_class_child(node, _, _) + or + ruby_do_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_do_block_child(node, _, _) + or + ruby_method_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_method_child(node, _, _) + or + ruby_module_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_module_child(node, _, _) + or + ruby_singleton_class_def(node, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_class_child(node, _, _) + or + ruby_singleton_method_def(node, _, _) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "end") and + ruby_singleton_method_child(node, _, _) + or + ruby_block_def(node) and + ruby_ast_node_info(body, node, _, _) and + ruby_tokeninfo(body, _, "}") and + ruby_block_child(node, _, _) + ) +} + +from AstNode token, int kind, string value +where ruby_tokeninfo(token, kind, value) and not body_statement(token) +select token, kind, value diff --git a/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/upgrade.properties b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/upgrade.properties new file mode 100644 index 00000000000..6793ae3c109 --- /dev/null +++ b/ruby/ql/lib/upgrades/4ba51641799d2aaa315c7323931e2dd2a94c9f9d/upgrade.properties @@ -0,0 +1,33 @@ +description: Wrap class, module, method, and block bodies in a named node +compatibility: partial + +ruby_block_body.rel: run ruby_block_body.qlo +ruby_block_body_def.rel: run ruby_block_body_def.qlo +ruby_block_body_child.rel: run ruby_block_body_child.qlo +ruby_block_child.rel: delete + +ruby_body_statement_child.rel: run ruby_body_statement_child.qlo +ruby_body_statement_def.rel: run ruby_body_statement_def.qlo + +ruby_class_body.rel: run ruby_class_body.qlo +ruby_class_child.rel: delete + +ruby_do_block_body.rel: run ruby_do_block_body.qlo +ruby_do_block_child.rel: delete + +ruby_method_body.rel: run ruby_method_body.qlo +ruby_method_child.rel: delete + +ruby_module_body.rel: run ruby_module_body.qlo +ruby_module_child.rel: delete + +ruby_singleton_class_body.rel: run ruby_singleton_class_body.qlo +ruby_singleton_class_child.rel: delete + +ruby_singleton_method_body.rel: run ruby_singleton_method_body.qlo +ruby_singleton_method_child.rel: delete + +ruby_ast_node_info.rel: run ruby_ast_node_info.qlo +ruby_tokeninfo.rel: run ruby_tokeninfo.qlo + +locations_default.rel: run locations_default.qlo \ No newline at end of file diff --git a/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql index cf470213584..137bfc94163 100644 --- a/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql +++ b/ruby/ql/src/queries/security/cwe-295/RequestWithoutValidation.ql @@ -16,7 +16,7 @@ import codeql.ruby.Concepts import codeql.ruby.DataFlow from - HTTP::Client::Request request, DataFlow::Node disablingNode, DataFlow::Node origin, string ending + Http::Client::Request request, DataFlow::Node disablingNode, DataFlow::Node origin, string ending where request.disablesCertificateValidation(disablingNode, origin) and // Showing the origin is only useful when it's a different node than the one disabling diff --git a/ruby/ql/test/library-tests/ast/Ast.expected b/ruby/ql/test/library-tests/ast/Ast.expected index 4cb29e0f033..33229c64393 100644 --- a/ruby/ql/test/library-tests/ast/Ast.expected +++ b/ruby/ql/test/library-tests/ast/Ast.expected @@ -2260,12 +2260,8 @@ literals/literals.rb: # 189| getAnOperand/getLeftOperand: [LocalVariableAccess] query # 189| getAnOperand/getRightOperand: [HereDoc] <<'DOC' # 189| getComponent: [StringTextComponent] -# 189| text without -# 190| getComponent: [StringInterpolationComponent] #{...} -# 190| getStmt: [MethodCall] call to interpolation -# 190| getReceiver: [SelfVariableAccess] self -# 190| getComponent: [StringTextComponent] ! -# 190| +# 189| text without #{ interpolation } ! +# 189| # 193| getStmt: [AssignExpr] ... = ... # 193| getAnOperand/getLeftOperand: [LocalVariableAccess] output # 193| getAnOperand/getRightOperand: [HereDoc] <<`SCRIPT` @@ -2293,6 +2289,17 @@ literals/literals.rb: # 199| getKey: [SymbolLiteral] :Z # 199| getComponent: [StringTextComponent] Z # 199| getValue: [ConstantReadAccess] Z +# 201| getStmt: [StringLiteral] "@foo: #{...} @@bar: #{...} $_..." +# 201| getComponent: [StringTextComponent] @foo: +# 201| getComponent: [StringInterpolationComponent] #{...} +# 201| getStmt: [InstanceVariableAccess] @foo +# 201| getReceiver: [SelfVariableAccess] self +# 201| getComponent: [StringTextComponent] @@bar: +# 201| getComponent: [StringInterpolationComponent] #{...} +# 201| getStmt: [ClassVariableAccess] @@bar +# 201| getComponent: [StringTextComponent] $_: +# 201| getComponent: [StringInterpolationComponent] #{...} +# 201| getStmt: [GlobalVariableAccess] $_ control/loops.rb: # 1| [Toplevel] loops.rb # 2| getStmt: [AssignExpr] ... = ... diff --git a/ruby/ql/test/library-tests/ast/TreeSitter.expected b/ruby/ql/test/library-tests/ast/TreeSitter.expected index cb5a17ebebb..839ad55fb38 100644 --- a/ruby/ql/test/library-tests/ast/TreeSitter.expected +++ b/ruby/ql/test/library-tests/ast/TreeSitter.expected @@ -37,10 +37,11 @@ calls/calls.rb: # 17| 0: [ReservedWord] | # 17| 1: [Identifier] x # 17| 2: [ReservedWord] | -# 17| 2: [Binary] Binary -# 17| 0: [Identifier] x -# 17| 1: [ReservedWord] + -# 17| 2: [Integer] 1 +# 17| 2: [BlockBody] BlockBody +# 17| 0: [Binary] Binary +# 17| 0: [Identifier] x +# 17| 1: [ReservedWord] + +# 17| 2: [Integer] 1 # 17| 3: [ReservedWord] } # 20| 6: [Call] Call # 20| 0: [Identifier] foo @@ -50,10 +51,11 @@ calls/calls.rb: # 20| 0: [ReservedWord] | # 20| 1: [Identifier] x # 20| 2: [ReservedWord] | -# 21| 2: [Binary] Binary -# 21| 0: [Identifier] x -# 21| 1: [ReservedWord] + -# 21| 2: [Integer] 1 +# 21| 2: [BodyStatement] BodyStatement +# 21| 0: [Binary] Binary +# 21| 0: [Identifier] x +# 21| 1: [ReservedWord] + +# 21| 2: [Integer] 1 # 22| 3: [ReservedWord] end # 25| 7: [Call] Call # 25| 0: [Integer] 123 @@ -72,26 +74,29 @@ calls/calls.rb: # 25| 0: [ReservedWord] | # 25| 1: [Identifier] x # 25| 2: [ReservedWord] | -# 26| 2: [Binary] Binary -# 26| 0: [Identifier] x -# 26| 1: [ReservedWord] + -# 26| 2: [Integer] 1 +# 26| 2: [BodyStatement] BodyStatement +# 26| 0: [Binary] Binary +# 26| 0: [Identifier] x +# 26| 1: [ReservedWord] + +# 26| 2: [Integer] 1 # 27| 3: [ReservedWord] end # 30| 8: [Method] Method # 30| 0: [ReservedWord] def # 30| 1: [Identifier] method_that_yields -# 31| 2: [Yield] Yield -# 31| 0: [ReservedWord] yield +# 31| 2: [BodyStatement] BodyStatement +# 31| 0: [Yield] Yield +# 31| 0: [ReservedWord] yield # 32| 3: [ReservedWord] end # 35| 9: [Method] Method # 35| 0: [ReservedWord] def # 35| 1: [Identifier] another_method_that_yields -# 36| 2: [Yield] Yield -# 36| 0: [ReservedWord] yield -# 36| 1: [ArgumentList] ArgumentList -# 36| 0: [Integer] 100 -# 36| 1: [ReservedWord] , -# 36| 2: [Integer] 200 +# 36| 2: [BodyStatement] BodyStatement +# 36| 0: [Yield] Yield +# 36| 0: [ReservedWord] yield +# 36| 1: [ArgumentList] ArgumentList +# 36| 0: [Integer] 100 +# 36| 1: [ReservedWord] , +# 36| 2: [Integer] 200 # 37| 3: [ReservedWord] end # 46| 10: [Identifier] foo # 47| 11: [Call] Call @@ -218,13 +223,14 @@ calls/calls.rb: # 92| 1: [ReservedWord] ) # 92| 2: [Block] Block # 92| 0: [ReservedWord] { -# 92| 1: [Identifier] bar -# 92| 2: [ReservedWord] ; -# 92| 3: [Call] Call -# 92| 0: [Constant] X -# 92| 1: [ReservedWord] :: -# 92| 2: [Identifier] baz -# 92| 4: [ReservedWord] } +# 92| 1: [BlockBody] BlockBody +# 92| 0: [Identifier] bar +# 92| 1: [ReservedWord] ; +# 92| 2: [Call] Call +# 92| 0: [Constant] X +# 92| 1: [ReservedWord] :: +# 92| 2: [Identifier] baz +# 92| 2: [ReservedWord] } # 95| 30: [Call] Call # 95| 0: [Identifier] foo # 95| 1: [ArgumentList] ArgumentList @@ -232,12 +238,13 @@ calls/calls.rb: # 95| 1: [ReservedWord] ) # 95| 2: [DoBlock] DoBlock # 95| 0: [ReservedWord] do -# 96| 1: [Identifier] bar -# 97| 2: [Call] Call -# 97| 0: [Constant] X -# 97| 1: [ReservedWord] :: -# 97| 2: [Identifier] baz -# 98| 3: [ReservedWord] end +# 96| 1: [BodyStatement] BodyStatement +# 96| 0: [Identifier] bar +# 97| 1: [Call] Call +# 97| 0: [Constant] X +# 97| 1: [ReservedWord] :: +# 97| 2: [Identifier] baz +# 98| 2: [ReservedWord] end # 101| 31: [Call] Call # 101| 0: [Identifier] foo # 101| 1: [ReservedWord] . @@ -284,12 +291,13 @@ calls/calls.rb: # 116| 35: [Class] Class # 116| 0: [ReservedWord] class # 116| 1: [Constant] MyClass -# 117| 2: [Identifier] foo -# 118| 3: [Call] Call -# 118| 0: [Constant] X -# 118| 1: [ReservedWord] :: -# 118| 2: [Identifier] bar -# 119| 4: [ReservedWord] end +# 117| 2: [BodyStatement] BodyStatement +# 117| 0: [Identifier] foo +# 118| 1: [Call] Call +# 118| 0: [Constant] X +# 118| 1: [ReservedWord] :: +# 118| 2: [Identifier] bar +# 119| 3: [ReservedWord] end # 122| 36: [Class] Class # 122| 0: [ReservedWord] class # 122| 1: [Constant] MyClass @@ -311,7 +319,8 @@ calls/calls.rb: # 128| 0: [ReservedWord] class # 128| 1: [ReservedWord] << # 128| 2: [Identifier] foo -# 129| 3: [Identifier] bar +# 129| 3: [BodyStatement] BodyStatement +# 129| 0: [Identifier] bar # 130| 4: [ReservedWord] end # 131| 39: [SingletonClass] SingletonClass # 131| 0: [ReservedWord] class @@ -320,31 +329,34 @@ calls/calls.rb: # 131| 0: [Constant] X # 131| 1: [ReservedWord] :: # 131| 2: [Identifier] foo -# 132| 3: [Call] Call -# 132| 0: [Constant] X -# 132| 1: [ReservedWord] :: -# 132| 2: [Identifier] bar +# 132| 3: [BodyStatement] BodyStatement +# 132| 0: [Call] Call +# 132| 0: [Constant] X +# 132| 1: [ReservedWord] :: +# 132| 2: [Identifier] bar # 133| 4: [ReservedWord] end # 136| 40: [Method] Method # 136| 0: [ReservedWord] def # 136| 1: [Identifier] some_method -# 137| 2: [Identifier] foo -# 138| 3: [Call] Call -# 138| 0: [Constant] X -# 138| 1: [ReservedWord] :: -# 138| 2: [Identifier] bar -# 139| 4: [ReservedWord] end +# 137| 2: [BodyStatement] BodyStatement +# 137| 0: [Identifier] foo +# 138| 1: [Call] Call +# 138| 0: [Constant] X +# 138| 1: [ReservedWord] :: +# 138| 2: [Identifier] bar +# 139| 3: [ReservedWord] end # 142| 41: [SingletonMethod] SingletonMethod # 142| 0: [ReservedWord] def # 142| 1: [Identifier] foo # 142| 2: [ReservedWord] . # 142| 3: [Identifier] some_method -# 143| 4: [Identifier] bar -# 144| 5: [Call] Call -# 144| 0: [Constant] X -# 144| 1: [ReservedWord] :: -# 144| 2: [Identifier] baz -# 145| 6: [ReservedWord] end +# 143| 4: [BodyStatement] BodyStatement +# 143| 0: [Identifier] bar +# 144| 1: [Call] Call +# 144| 0: [Constant] X +# 144| 1: [ReservedWord] :: +# 144| 2: [Identifier] baz +# 145| 5: [ReservedWord] end # 148| 42: [Method] Method # 148| 0: [ReservedWord] def # 148| 1: [Identifier] method_with_keyword_param @@ -398,12 +410,13 @@ calls/calls.rb: # 160| 46: [Module] Module # 160| 0: [ReservedWord] module # 160| 1: [Constant] SomeModule -# 161| 2: [Identifier] foo -# 162| 3: [Call] Call -# 162| 0: [Constant] X -# 162| 1: [ReservedWord] :: -# 162| 2: [Identifier] bar -# 163| 4: [ReservedWord] end +# 161| 2: [BodyStatement] BodyStatement +# 161| 0: [Identifier] foo +# 162| 1: [Call] Call +# 162| 0: [Constant] X +# 162| 1: [ReservedWord] :: +# 162| 2: [Identifier] bar +# 163| 3: [ReservedWord] end # 166| 47: [Conditional] Conditional # 166| 0: [Identifier] foo # 166| 1: [ReservedWord] ? @@ -809,111 +822,119 @@ calls/calls.rb: # 284| 88: [Class] Class # 284| 0: [ReservedWord] class # 284| 1: [Constant] MyClass -# 285| 2: [Method] Method -# 285| 0: [ReservedWord] def -# 285| 1: [Identifier] my_method -# 286| 2: [Super] super -# 287| 3: [Call] Call -# 287| 0: [Super] super -# 287| 1: [ArgumentList] ArgumentList -# 287| 0: [ReservedWord] ( -# 287| 1: [ReservedWord] ) -# 288| 4: [Call] Call -# 288| 0: [Super] super -# 288| 1: [ArgumentList] ArgumentList -# 288| 0: [String] String -# 288| 0: [ReservedWord] ' -# 288| 1: [StringContent] blah -# 288| 2: [ReservedWord] ' -# 289| 5: [Call] Call -# 289| 0: [Super] super -# 289| 1: [ArgumentList] ArgumentList -# 289| 0: [Integer] 1 -# 289| 1: [ReservedWord] , -# 289| 2: [Integer] 2 -# 289| 3: [ReservedWord] , -# 289| 4: [Integer] 3 -# 290| 6: [Call] Call -# 290| 0: [Super] super -# 290| 1: [Block] Block -# 290| 0: [ReservedWord] { -# 290| 1: [BlockParameters] BlockParameters -# 290| 0: [ReservedWord] | -# 290| 1: [Identifier] x -# 290| 2: [ReservedWord] | -# 290| 2: [Binary] Binary -# 290| 0: [Identifier] x -# 290| 1: [ReservedWord] + -# 290| 2: [Integer] 1 -# 290| 3: [ReservedWord] } -# 291| 7: [Call] Call -# 291| 0: [Super] super -# 291| 1: [DoBlock] DoBlock -# 291| 0: [ReservedWord] do -# 291| 1: [BlockParameters] BlockParameters -# 291| 0: [ReservedWord] | -# 291| 1: [Identifier] x -# 291| 2: [ReservedWord] | -# 291| 2: [Binary] Binary -# 291| 0: [Identifier] x -# 291| 1: [ReservedWord] * -# 291| 2: [Integer] 2 -# 291| 3: [ReservedWord] end -# 292| 8: [Call] Call -# 292| 0: [Super] super -# 292| 1: [ArgumentList] ArgumentList -# 292| 0: [Integer] 4 -# 292| 1: [ReservedWord] , -# 292| 2: [Integer] 5 -# 292| 2: [Block] Block -# 292| 0: [ReservedWord] { -# 292| 1: [BlockParameters] BlockParameters -# 292| 0: [ReservedWord] | -# 292| 1: [Identifier] x -# 292| 2: [ReservedWord] | -# 292| 2: [Binary] Binary -# 292| 0: [Identifier] x -# 292| 1: [ReservedWord] + -# 292| 2: [Integer] 100 -# 292| 3: [ReservedWord] } -# 293| 9: [Call] Call -# 293| 0: [Super] super -# 293| 1: [ArgumentList] ArgumentList -# 293| 0: [Integer] 6 -# 293| 1: [ReservedWord] , -# 293| 2: [Integer] 7 -# 293| 2: [DoBlock] DoBlock -# 293| 0: [ReservedWord] do -# 293| 1: [BlockParameters] BlockParameters -# 293| 0: [ReservedWord] | -# 293| 1: [Identifier] x -# 293| 2: [ReservedWord] | -# 293| 2: [Binary] Binary -# 293| 0: [Identifier] x -# 293| 1: [ReservedWord] + -# 293| 2: [Integer] 200 -# 293| 3: [ReservedWord] end -# 294| 10: [ReservedWord] end +# 285| 2: [BodyStatement] BodyStatement +# 285| 0: [Method] Method +# 285| 0: [ReservedWord] def +# 285| 1: [Identifier] my_method +# 286| 2: [BodyStatement] BodyStatement +# 286| 0: [Super] super +# 287| 1: [Call] Call +# 287| 0: [Super] super +# 287| 1: [ArgumentList] ArgumentList +# 287| 0: [ReservedWord] ( +# 287| 1: [ReservedWord] ) +# 288| 2: [Call] Call +# 288| 0: [Super] super +# 288| 1: [ArgumentList] ArgumentList +# 288| 0: [String] String +# 288| 0: [ReservedWord] ' +# 288| 1: [StringContent] blah +# 288| 2: [ReservedWord] ' +# 289| 3: [Call] Call +# 289| 0: [Super] super +# 289| 1: [ArgumentList] ArgumentList +# 289| 0: [Integer] 1 +# 289| 1: [ReservedWord] , +# 289| 2: [Integer] 2 +# 289| 3: [ReservedWord] , +# 289| 4: [Integer] 3 +# 290| 4: [Call] Call +# 290| 0: [Super] super +# 290| 1: [Block] Block +# 290| 0: [ReservedWord] { +# 290| 1: [BlockParameters] BlockParameters +# 290| 0: [ReservedWord] | +# 290| 1: [Identifier] x +# 290| 2: [ReservedWord] | +# 290| 2: [BlockBody] BlockBody +# 290| 0: [Binary] Binary +# 290| 0: [Identifier] x +# 290| 1: [ReservedWord] + +# 290| 2: [Integer] 1 +# 290| 3: [ReservedWord] } +# 291| 5: [Call] Call +# 291| 0: [Super] super +# 291| 1: [DoBlock] DoBlock +# 291| 0: [ReservedWord] do +# 291| 1: [BlockParameters] BlockParameters +# 291| 0: [ReservedWord] | +# 291| 1: [Identifier] x +# 291| 2: [ReservedWord] | +# 291| 2: [BodyStatement] BodyStatement +# 291| 0: [Binary] Binary +# 291| 0: [Identifier] x +# 291| 1: [ReservedWord] * +# 291| 2: [Integer] 2 +# 291| 3: [ReservedWord] end +# 292| 6: [Call] Call +# 292| 0: [Super] super +# 292| 1: [ArgumentList] ArgumentList +# 292| 0: [Integer] 4 +# 292| 1: [ReservedWord] , +# 292| 2: [Integer] 5 +# 292| 2: [Block] Block +# 292| 0: [ReservedWord] { +# 292| 1: [BlockParameters] BlockParameters +# 292| 0: [ReservedWord] | +# 292| 1: [Identifier] x +# 292| 2: [ReservedWord] | +# 292| 2: [BlockBody] BlockBody +# 292| 0: [Binary] Binary +# 292| 0: [Identifier] x +# 292| 1: [ReservedWord] + +# 292| 2: [Integer] 100 +# 292| 3: [ReservedWord] } +# 293| 7: [Call] Call +# 293| 0: [Super] super +# 293| 1: [ArgumentList] ArgumentList +# 293| 0: [Integer] 6 +# 293| 1: [ReservedWord] , +# 293| 2: [Integer] 7 +# 293| 2: [DoBlock] DoBlock +# 293| 0: [ReservedWord] do +# 293| 1: [BlockParameters] BlockParameters +# 293| 0: [ReservedWord] | +# 293| 1: [Identifier] x +# 293| 2: [ReservedWord] | +# 293| 2: [BodyStatement] BodyStatement +# 293| 0: [Binary] Binary +# 293| 0: [Identifier] x +# 293| 1: [ReservedWord] + +# 293| 2: [Integer] 200 +# 293| 3: [ReservedWord] end +# 294| 3: [ReservedWord] end # 295| 3: [ReservedWord] end # 301| 89: [Class] Class # 301| 0: [ReservedWord] class # 301| 1: [Constant] AnotherClass -# 302| 2: [Method] Method -# 302| 0: [ReservedWord] def -# 302| 1: [Identifier] another_method -# 303| 2: [Call] Call -# 303| 0: [Identifier] foo -# 303| 1: [ReservedWord] . -# 303| 2: [Identifier] super -# 304| 3: [Call] Call -# 304| 0: [Self] self -# 304| 1: [ReservedWord] . -# 304| 2: [Identifier] super -# 305| 4: [Call] Call -# 305| 0: [Super] super -# 305| 1: [ReservedWord] . -# 305| 2: [Identifier] super -# 306| 5: [ReservedWord] end +# 302| 2: [BodyStatement] BodyStatement +# 302| 0: [Method] Method +# 302| 0: [ReservedWord] def +# 302| 1: [Identifier] another_method +# 303| 2: [BodyStatement] BodyStatement +# 303| 0: [Call] Call +# 303| 0: [Identifier] foo +# 303| 1: [ReservedWord] . +# 303| 2: [Identifier] super +# 304| 1: [Call] Call +# 304| 0: [Self] self +# 304| 1: [ReservedWord] . +# 304| 2: [Identifier] super +# 305| 2: [Call] Call +# 305| 0: [Super] super +# 305| 1: [ReservedWord] . +# 305| 2: [Identifier] super +# 306| 3: [ReservedWord] end # 307| 3: [ReservedWord] end # 310| 90: [Call] Call # 310| 0: [Identifier] foo @@ -1100,13 +1121,14 @@ calls/calls.rb: # 331| 1: [ForwardParameter] ... # 331| 0: [ReservedWord] ... # 331| 2: [ReservedWord] ) -# 332| 3: [Call] Call -# 332| 0: [Super] super -# 332| 1: [ArgumentList] ArgumentList -# 332| 0: [ReservedWord] ( -# 332| 1: [ForwardArgument] ... -# 332| 0: [ReservedWord] ... -# 332| 2: [ReservedWord] ) +# 332| 3: [BodyStatement] BodyStatement +# 332| 0: [Call] Call +# 332| 0: [Super] super +# 332| 1: [ArgumentList] ArgumentList +# 332| 0: [ReservedWord] ( +# 332| 1: [ForwardArgument] ... +# 332| 0: [ReservedWord] ... +# 332| 2: [ReservedWord] ) # 333| 4: [ReservedWord] end # 335| 106: [Method] Method # 335| 0: [ReservedWord] def @@ -1120,15 +1142,16 @@ calls/calls.rb: # 335| 5: [ForwardParameter] ... # 335| 0: [ReservedWord] ... # 335| 6: [ReservedWord] ) -# 336| 3: [Call] Call -# 336| 0: [Identifier] bar -# 336| 1: [ArgumentList] ArgumentList -# 336| 0: [ReservedWord] ( -# 336| 1: [Identifier] b -# 336| 2: [ReservedWord] , -# 336| 3: [ForwardArgument] ... -# 336| 0: [ReservedWord] ... -# 336| 4: [ReservedWord] ) +# 336| 3: [BodyStatement] BodyStatement +# 336| 0: [Call] Call +# 336| 0: [Identifier] bar +# 336| 1: [ArgumentList] ArgumentList +# 336| 0: [ReservedWord] ( +# 336| 1: [Identifier] b +# 336| 2: [ReservedWord] , +# 336| 3: [ForwardArgument] ... +# 336| 0: [ReservedWord] ... +# 336| 4: [ReservedWord] ) # 337| 4: [ReservedWord] end # 340| 107: [For] For # 340| 0: [ReservedWord] for @@ -1223,7 +1246,8 @@ calls/calls.rb: # 351| 2: [ReservedWord] ) # 351| 2: [Block] Block # 351| 0: [ReservedWord] { -# 351| 1: [Identifier] y +# 351| 1: [BlockBody] BlockBody +# 351| 0: [Identifier] y # 351| 2: [ReservedWord] } # 352| 114: [Assignment] Assignment # 352| 0: [Identifier] f @@ -1236,10 +1260,11 @@ calls/calls.rb: # 352| 2: [ReservedWord] ) # 352| 2: [Block] Block # 352| 0: [ReservedWord] { -# 352| 1: [Call] Call -# 352| 0: [Identifier] foo -# 352| 1: [ArgumentList] ArgumentList -# 352| 0: [Identifier] x +# 352| 1: [BlockBody] BlockBody +# 352| 0: [Call] Call +# 352| 0: [Identifier] foo +# 352| 1: [ArgumentList] ArgumentList +# 352| 0: [Identifier] x # 352| 2: [ReservedWord] } # 353| 115: [Assignment] Assignment # 353| 0: [Identifier] g @@ -1252,7 +1277,8 @@ calls/calls.rb: # 353| 2: [ReservedWord] ) # 353| 2: [Block] Block # 353| 0: [ReservedWord] { -# 353| 1: [Identifier] unknown_call +# 353| 1: [BlockBody] BlockBody +# 353| 0: [Identifier] unknown_call # 353| 2: [ReservedWord] } # 354| 116: [Assignment] Assignment # 354| 0: [Identifier] h @@ -1265,10 +1291,11 @@ calls/calls.rb: # 354| 2: [ReservedWord] ) # 354| 2: [DoBlock] DoBlock # 354| 0: [ReservedWord] do -# 355| 1: [Identifier] x -# 356| 2: [Identifier] y -# 357| 3: [Identifier] unknown_call -# 358| 4: [ReservedWord] end +# 355| 1: [BodyStatement] BodyStatement +# 355| 0: [Identifier] x +# 356| 1: [Identifier] y +# 357| 2: [Identifier] unknown_call +# 358| 2: [ReservedWord] end # 361| 117: [Call] Call # 361| 0: [Identifier] list # 361| 1: [ReservedWord] . @@ -1297,7 +1324,8 @@ calls/calls.rb: # 364| 0: [ReservedWord] | # 364| 1: [Identifier] x # 364| 2: [ReservedWord] | -# 364| 2: [Identifier] x +# 364| 2: [BlockBody] BlockBody +# 364| 0: [Identifier] x # 364| 3: [ReservedWord] } # 1| [Comment] # call with no receiver, arguments, or block # 4| [Comment] # call whose name is a scope resolution @@ -1378,49 +1406,52 @@ constants/constants.rb: # 1| 0: [Module] Module # 1| 0: [ReservedWord] module # 1| 1: [Constant] ModuleA -# 2| 2: [Class] Class -# 2| 0: [ReservedWord] class -# 2| 1: [Constant] ClassA -# 3| 2: [Assignment] Assignment -# 3| 0: [Constant] CONST_A -# 3| 1: [ReservedWord] = -# 3| 2: [String] String -# 3| 0: [ReservedWord] " -# 3| 1: [StringContent] const_a -# 3| 2: [ReservedWord] " -# 4| 3: [ReservedWord] end -# 6| 3: [Assignment] Assignment -# 6| 0: [Constant] CONST_B -# 6| 1: [ReservedWord] = -# 6| 2: [String] String -# 6| 0: [ReservedWord] " -# 6| 1: [StringContent] const_b -# 6| 2: [ReservedWord] " -# 8| 4: [Module] Module -# 8| 0: [ReservedWord] module -# 8| 1: [Constant] ModuleB -# 9| 2: [Class] Class -# 9| 0: [ReservedWord] class -# 9| 1: [Constant] ClassB -# 9| 2: [Superclass] Superclass -# 9| 0: [ReservedWord] < -# 9| 1: [Constant] Base -# 10| 3: [ReservedWord] end -# 12| 3: [Class] Class -# 12| 0: [ReservedWord] class -# 12| 1: [Constant] ClassC -# 12| 2: [Superclass] Superclass -# 12| 0: [ReservedWord] < -# 12| 1: [ScopeResolution] ScopeResolution -# 12| 0: [ScopeResolution] ScopeResolution -# 12| 0: [Constant] X -# 12| 1: [ReservedWord] :: -# 12| 2: [Constant] Y -# 12| 1: [ReservedWord] :: -# 12| 2: [Constant] Z -# 13| 3: [ReservedWord] end -# 14| 4: [ReservedWord] end -# 15| 5: [ReservedWord] end +# 2| 2: [BodyStatement] BodyStatement +# 2| 0: [Class] Class +# 2| 0: [ReservedWord] class +# 2| 1: [Constant] ClassA +# 3| 2: [BodyStatement] BodyStatement +# 3| 0: [Assignment] Assignment +# 3| 0: [Constant] CONST_A +# 3| 1: [ReservedWord] = +# 3| 2: [String] String +# 3| 0: [ReservedWord] " +# 3| 1: [StringContent] const_a +# 3| 2: [ReservedWord] " +# 4| 3: [ReservedWord] end +# 6| 1: [Assignment] Assignment +# 6| 0: [Constant] CONST_B +# 6| 1: [ReservedWord] = +# 6| 2: [String] String +# 6| 0: [ReservedWord] " +# 6| 1: [StringContent] const_b +# 6| 2: [ReservedWord] " +# 8| 2: [Module] Module +# 8| 0: [ReservedWord] module +# 8| 1: [Constant] ModuleB +# 9| 2: [BodyStatement] BodyStatement +# 9| 0: [Class] Class +# 9| 0: [ReservedWord] class +# 9| 1: [Constant] ClassB +# 9| 2: [Superclass] Superclass +# 9| 0: [ReservedWord] < +# 9| 1: [Constant] Base +# 10| 3: [ReservedWord] end +# 12| 1: [Class] Class +# 12| 0: [ReservedWord] class +# 12| 1: [Constant] ClassC +# 12| 2: [Superclass] Superclass +# 12| 0: [ReservedWord] < +# 12| 1: [ScopeResolution] ScopeResolution +# 12| 0: [ScopeResolution] ScopeResolution +# 12| 0: [Constant] X +# 12| 1: [ReservedWord] :: +# 12| 2: [Constant] Y +# 12| 1: [ReservedWord] :: +# 12| 2: [Constant] Z +# 13| 3: [ReservedWord] end +# 14| 3: [ReservedWord] end +# 15| 3: [ReservedWord] end # 17| 1: [Assignment] Assignment # 17| 0: [Constant] GREETING # 17| 1: [ReservedWord] = @@ -1446,62 +1477,64 @@ constants/constants.rb: # 19| 2: [Method] Method # 19| 0: [ReservedWord] def # 19| 1: [Identifier] foo -# 20| 2: [Assignment] Assignment -# 20| 0: [Constant] Names -# 20| 1: [ReservedWord] = -# 20| 2: [Array] Array -# 20| 0: [ReservedWord] [ -# 20| 1: [String] String -# 20| 0: [ReservedWord] ' -# 20| 1: [StringContent] Vera -# 20| 2: [ReservedWord] ' -# 20| 2: [ReservedWord] , -# 20| 3: [String] String -# 20| 0: [ReservedWord] ' -# 20| 1: [StringContent] Chuck -# 20| 2: [ReservedWord] ' -# 20| 4: [ReservedWord] , -# 20| 5: [String] String -# 20| 0: [ReservedWord] ' -# 20| 1: [StringContent] Dave -# 20| 2: [ReservedWord] ' -# 20| 6: [ReservedWord] ] -# 22| 3: [Call] Call -# 22| 0: [Constant] Names -# 22| 1: [ReservedWord] . -# 22| 2: [Identifier] each -# 22| 3: [DoBlock] DoBlock -# 22| 0: [ReservedWord] do -# 22| 1: [BlockParameters] BlockParameters -# 22| 0: [ReservedWord] | -# 22| 1: [Identifier] name -# 22| 2: [ReservedWord] | -# 23| 2: [Call] Call -# 23| 0: [Identifier] puts -# 23| 1: [ArgumentList] ArgumentList -# 23| 0: [String] String -# 23| 0: [ReservedWord] " -# 23| 1: [Interpolation] Interpolation -# 23| 0: [ReservedWord] #{ -# 23| 1: [Constant] GREETING -# 23| 2: [ReservedWord] } -# 23| 2: [StringContent] -# 23| 3: [Interpolation] Interpolation -# 23| 0: [ReservedWord] #{ -# 23| 1: [Identifier] name -# 23| 2: [ReservedWord] } -# 23| 4: [ReservedWord] " -# 24| 3: [ReservedWord] end -# 28| 4: [Call] Call -# 28| 0: [Constant] Array -# 28| 1: [ArgumentList] ArgumentList -# 28| 0: [ReservedWord] ( -# 28| 1: [String] String -# 28| 0: [ReservedWord] ' -# 28| 1: [StringContent] foo -# 28| 2: [ReservedWord] ' -# 28| 2: [ReservedWord] ) -# 29| 5: [ReservedWord] end +# 20| 2: [BodyStatement] BodyStatement +# 20| 0: [Assignment] Assignment +# 20| 0: [Constant] Names +# 20| 1: [ReservedWord] = +# 20| 2: [Array] Array +# 20| 0: [ReservedWord] [ +# 20| 1: [String] String +# 20| 0: [ReservedWord] ' +# 20| 1: [StringContent] Vera +# 20| 2: [ReservedWord] ' +# 20| 2: [ReservedWord] , +# 20| 3: [String] String +# 20| 0: [ReservedWord] ' +# 20| 1: [StringContent] Chuck +# 20| 2: [ReservedWord] ' +# 20| 4: [ReservedWord] , +# 20| 5: [String] String +# 20| 0: [ReservedWord] ' +# 20| 1: [StringContent] Dave +# 20| 2: [ReservedWord] ' +# 20| 6: [ReservedWord] ] +# 22| 1: [Call] Call +# 22| 0: [Constant] Names +# 22| 1: [ReservedWord] . +# 22| 2: [Identifier] each +# 22| 3: [DoBlock] DoBlock +# 22| 0: [ReservedWord] do +# 22| 1: [BlockParameters] BlockParameters +# 22| 0: [ReservedWord] | +# 22| 1: [Identifier] name +# 22| 2: [ReservedWord] | +# 23| 2: [BodyStatement] BodyStatement +# 23| 0: [Call] Call +# 23| 0: [Identifier] puts +# 23| 1: [ArgumentList] ArgumentList +# 23| 0: [String] String +# 23| 0: [ReservedWord] " +# 23| 1: [Interpolation] Interpolation +# 23| 0: [ReservedWord] #{ +# 23| 1: [Constant] GREETING +# 23| 2: [ReservedWord] } +# 23| 2: [StringContent] +# 23| 3: [Interpolation] Interpolation +# 23| 0: [ReservedWord] #{ +# 23| 1: [Identifier] name +# 23| 2: [ReservedWord] } +# 23| 4: [ReservedWord] " +# 24| 3: [ReservedWord] end +# 28| 2: [Call] Call +# 28| 0: [Constant] Array +# 28| 1: [ArgumentList] ArgumentList +# 28| 0: [ReservedWord] ( +# 28| 1: [String] String +# 28| 0: [ReservedWord] ' +# 28| 1: [StringContent] foo +# 28| 2: [ReservedWord] ' +# 28| 2: [ReservedWord] ) +# 29| 3: [ReservedWord] end # 31| 3: [Class] Class # 31| 0: [ReservedWord] class # 31| 1: [ScopeResolution] ScopeResolution @@ -1514,10 +1547,11 @@ constants/constants.rb: # 31| 0: [Constant] ModuleA # 31| 1: [ReservedWord] :: # 31| 2: [Constant] ClassA -# 32| 3: [Assignment] Assignment -# 32| 0: [Constant] FOURTY_TWO -# 32| 1: [ReservedWord] = -# 32| 2: [Integer] 42 +# 32| 3: [BodyStatement] BodyStatement +# 32| 0: [Assignment] Assignment +# 32| 0: [Constant] FOURTY_TWO +# 32| 1: [ReservedWord] = +# 32| 2: [Integer] 42 # 33| 4: [ReservedWord] end # 35| 4: [Module] Module # 35| 0: [ReservedWord] module @@ -1525,10 +1559,11 @@ constants/constants.rb: # 35| 0: [Constant] ModuleA # 35| 1: [ReservedWord] :: # 35| 2: [Constant] ModuleC -# 36| 2: [Assignment] Assignment -# 36| 0: [Constant] FOURTY_THREE -# 36| 1: [ReservedWord] = -# 36| 2: [Integer] 43 +# 36| 2: [BodyStatement] BodyStatement +# 36| 0: [Assignment] Assignment +# 36| 0: [Constant] FOURTY_THREE +# 36| 1: [ReservedWord] = +# 36| 2: [Integer] 43 # 37| 3: [ReservedWord] end # 39| 5: [Assignment] Assignment # 39| 0: [ScopeResolution] ScopeResolution @@ -1566,96 +1601,104 @@ constants/constants.rb: # 46| 0: [Constant] ModuleA # 46| 1: [ReservedWord] :: # 46| 2: [Constant] ModuleB -# 47| 2: [Class] Class -# 47| 0: [ReservedWord] class -# 47| 1: [Constant] ClassB -# 47| 2: [Superclass] Superclass -# 47| 0: [ReservedWord] < -# 47| 1: [Constant] Base -# 48| 3: [Assignment] Assignment -# 48| 0: [Constant] FOURTY_ONE -# 48| 1: [ReservedWord] = -# 48| 2: [Integer] 41 -# 49| 4: [ReservedWord] end +# 47| 2: [BodyStatement] BodyStatement +# 47| 0: [Class] Class +# 47| 0: [ReservedWord] class +# 47| 1: [Constant] ClassB +# 47| 2: [Superclass] Superclass +# 47| 0: [ReservedWord] < +# 47| 1: [Constant] Base +# 48| 3: [BodyStatement] BodyStatement +# 48| 0: [Assignment] Assignment +# 48| 0: [Constant] FOURTY_ONE +# 48| 1: [ReservedWord] = +# 48| 2: [Integer] 41 +# 49| 4: [ReservedWord] end # 50| 3: [ReservedWord] end # 52| 10: [Module] Module # 52| 0: [ReservedWord] module # 52| 1: [Constant] ModuleA -# 53| 2: [Assignment] Assignment -# 53| 0: [Constant] FOURTY_FOUR -# 53| 1: [ReservedWord] = -# 53| 2: [String] String -# 53| 0: [ReservedWord] " -# 53| 1: [StringContent] fourty-four -# 53| 2: [ReservedWord] " -# 54| 3: [Class] Class -# 54| 0: [ReservedWord] class -# 54| 1: [ScopeResolution] ScopeResolution -# 54| 0: [Constant] ModuleB -# 54| 1: [ReservedWord] :: -# 54| 2: [Constant] ClassB -# 54| 2: [Superclass] Superclass -# 54| 0: [ReservedWord] < -# 54| 1: [Constant] Base -# 55| 3: [Assignment] Assignment -# 55| 0: [ClassVariable] @@fourty_four -# 55| 1: [ReservedWord] = -# 55| 2: [Constant] FOURTY_FOUR -# 56| 4: [Assignment] Assignment -# 56| 0: [Constant] FOURTY_FOUR -# 56| 1: [ReservedWord] = -# 56| 2: [Integer] 44 -# 57| 5: [Assignment] Assignment -# 57| 0: [ClassVariable] @@fourty_four -# 57| 1: [ReservedWord] = -# 57| 2: [Constant] FOURTY_FOUR -# 58| 6: [ReservedWord] end -# 59| 4: [ReservedWord] end +# 53| 2: [BodyStatement] BodyStatement +# 53| 0: [Assignment] Assignment +# 53| 0: [Constant] FOURTY_FOUR +# 53| 1: [ReservedWord] = +# 53| 2: [String] String +# 53| 0: [ReservedWord] " +# 53| 1: [StringContent] fourty-four +# 53| 2: [ReservedWord] " +# 54| 1: [Class] Class +# 54| 0: [ReservedWord] class +# 54| 1: [ScopeResolution] ScopeResolution +# 54| 0: [Constant] ModuleB +# 54| 1: [ReservedWord] :: +# 54| 2: [Constant] ClassB +# 54| 2: [Superclass] Superclass +# 54| 0: [ReservedWord] < +# 54| 1: [Constant] Base +# 55| 3: [BodyStatement] BodyStatement +# 55| 0: [Assignment] Assignment +# 55| 0: [ClassVariable] @@fourty_four +# 55| 1: [ReservedWord] = +# 55| 2: [Constant] FOURTY_FOUR +# 56| 1: [Assignment] Assignment +# 56| 0: [Constant] FOURTY_FOUR +# 56| 1: [ReservedWord] = +# 56| 2: [Integer] 44 +# 57| 2: [Assignment] Assignment +# 57| 0: [ClassVariable] @@fourty_four +# 57| 1: [ReservedWord] = +# 57| 2: [Constant] FOURTY_FOUR +# 58| 4: [ReservedWord] end +# 59| 3: [ReservedWord] end # 61| 11: [Module] Module # 61| 0: [ReservedWord] module # 61| 1: [Constant] Mod1 -# 62| 2: [Module] Module -# 62| 0: [ReservedWord] module -# 62| 1: [Constant] Mod3 -# 63| 2: [Assignment] Assignment -# 63| 0: [Constant] FOURTY_FIVE -# 63| 1: [ReservedWord] = -# 63| 2: [Integer] 45 -# 64| 3: [ReservedWord] end -# 65| 3: [Assignment] Assignment -# 65| 0: [ClassVariable] @@fourty_five -# 65| 1: [ReservedWord] = -# 65| 2: [ScopeResolution] ScopeResolution -# 65| 0: [Constant] Mod3 -# 65| 1: [ReservedWord] :: -# 65| 2: [Constant] FOURTY_FIVE -# 66| 4: [ReservedWord] end +# 62| 2: [BodyStatement] BodyStatement +# 62| 0: [Module] Module +# 62| 0: [ReservedWord] module +# 62| 1: [Constant] Mod3 +# 63| 2: [BodyStatement] BodyStatement +# 63| 0: [Assignment] Assignment +# 63| 0: [Constant] FOURTY_FIVE +# 63| 1: [ReservedWord] = +# 63| 2: [Integer] 45 +# 64| 3: [ReservedWord] end +# 65| 1: [Assignment] Assignment +# 65| 0: [ClassVariable] @@fourty_five +# 65| 1: [ReservedWord] = +# 65| 2: [ScopeResolution] ScopeResolution +# 65| 0: [Constant] Mod3 +# 65| 1: [ReservedWord] :: +# 65| 2: [Constant] FOURTY_FIVE +# 66| 3: [ReservedWord] end # 68| 12: [Module] Module # 68| 0: [ReservedWord] module # 68| 1: [Constant] Mod4 -# 69| 2: [Call] Call -# 69| 0: [Identifier] include -# 69| 1: [ArgumentList] ArgumentList -# 69| 0: [Constant] Mod1 -# 70| 3: [Module] Module -# 70| 0: [ReservedWord] module -# 70| 1: [ScopeResolution] ScopeResolution -# 70| 0: [Constant] Mod3 -# 70| 1: [ReservedWord] :: -# 70| 2: [Constant] Mod5 -# 71| 2: [Assignment] Assignment -# 71| 0: [Constant] FOURTY_SIX -# 71| 1: [ReservedWord] = -# 71| 2: [Integer] 46 -# 72| 3: [ReservedWord] end -# 73| 4: [Assignment] Assignment -# 73| 0: [ClassVariable] @@fourty_six -# 73| 1: [ReservedWord] = -# 73| 2: [ScopeResolution] ScopeResolution -# 73| 0: [Constant] Mod3 -# 73| 1: [ReservedWord] :: -# 73| 2: [Constant] FOURTY_SIX -# 74| 5: [ReservedWord] end +# 69| 2: [BodyStatement] BodyStatement +# 69| 0: [Call] Call +# 69| 0: [Identifier] include +# 69| 1: [ArgumentList] ArgumentList +# 69| 0: [Constant] Mod1 +# 70| 1: [Module] Module +# 70| 0: [ReservedWord] module +# 70| 1: [ScopeResolution] ScopeResolution +# 70| 0: [Constant] Mod3 +# 70| 1: [ReservedWord] :: +# 70| 2: [Constant] Mod5 +# 71| 2: [BodyStatement] BodyStatement +# 71| 0: [Assignment] Assignment +# 71| 0: [Constant] FOURTY_SIX +# 71| 1: [ReservedWord] = +# 71| 2: [Integer] 46 +# 72| 3: [ReservedWord] end +# 73| 2: [Assignment] Assignment +# 73| 0: [ClassVariable] @@fourty_six +# 73| 1: [ReservedWord] = +# 73| 2: [ScopeResolution] ScopeResolution +# 73| 0: [Constant] Mod3 +# 73| 1: [ReservedWord] :: +# 73| 2: [Constant] FOURTY_SIX +# 74| 3: [ReservedWord] end # 78| 13: [Assignment] Assignment # 78| 0: [Identifier] a # 78| 1: [ReservedWord] = @@ -2318,10 +2361,11 @@ control/cases.rb: # 101| 0: [Identifier] x # 101| 2: [Block] Block # 101| 0: [ReservedWord] { -# 101| 1: [Binary] Binary -# 101| 0: [Identifier] x -# 101| 1: [ReservedWord] == -# 101| 2: [Integer] 10 +# 101| 1: [BlockBody] BlockBody +# 101| 0: [Binary] Binary +# 101| 0: [Identifier] x +# 101| 1: [ReservedWord] == +# 101| 2: [Integer] 10 # 101| 2: [ReservedWord] } # 102| 17: [InClause] InClause # 102| 0: [ReservedWord] in @@ -3582,49 +3626,52 @@ gems/Gemfile: # 5| 2: [ReservedWord] ' # 5| 2: [DoBlock] DoBlock # 5| 0: [ReservedWord] do -# 6| 1: [Call] Call -# 6| 0: [Identifier] gem -# 6| 1: [ArgumentList] ArgumentList -# 6| 0: [String] String -# 6| 0: [ReservedWord] ' -# 6| 1: [StringContent] my_gem -# 6| 2: [ReservedWord] ' -# 6| 1: [ReservedWord] , -# 6| 2: [String] String -# 6| 0: [ReservedWord] ' -# 6| 1: [StringContent] 1.0 -# 6| 2: [ReservedWord] ' -# 7| 2: [Call] Call -# 7| 0: [Identifier] gem -# 7| 1: [ArgumentList] ArgumentList -# 7| 0: [String] String -# 7| 0: [ReservedWord] ' -# 7| 1: [StringContent] another_gem -# 7| 2: [ReservedWord] ' -# 7| 1: [ReservedWord] , -# 7| 2: [String] String -# 7| 0: [ReservedWord] ' -# 7| 1: [StringContent] 3.1.4 -# 7| 2: [ReservedWord] ' -# 8| 3: [ReservedWord] end +# 6| 1: [BodyStatement] BodyStatement +# 6| 0: [Call] Call +# 6| 0: [Identifier] gem +# 6| 1: [ArgumentList] ArgumentList +# 6| 0: [String] String +# 6| 0: [ReservedWord] ' +# 6| 1: [StringContent] my_gem +# 6| 2: [ReservedWord] ' +# 6| 1: [ReservedWord] , +# 6| 2: [String] String +# 6| 0: [ReservedWord] ' +# 6| 1: [StringContent] 1.0 +# 6| 2: [ReservedWord] ' +# 7| 1: [Call] Call +# 7| 0: [Identifier] gem +# 7| 1: [ArgumentList] ArgumentList +# 7| 0: [String] String +# 7| 0: [ReservedWord] ' +# 7| 1: [StringContent] another_gem +# 7| 2: [ReservedWord] ' +# 7| 1: [ReservedWord] , +# 7| 2: [String] String +# 7| 0: [ReservedWord] ' +# 7| 1: [StringContent] 3.1.4 +# 7| 2: [ReservedWord] ' +# 8| 2: [ReservedWord] end gems/lib/test.rb: # 1| [Program] Program # 1| 0: [Class] Class # 1| 0: [ReservedWord] class # 1| 1: [Constant] Foo -# 2| 2: [SingletonMethod] SingletonMethod -# 2| 0: [ReservedWord] def -# 2| 1: [Self] self -# 2| 2: [ReservedWord] . -# 2| 3: [Identifier] greet -# 3| 4: [Call] Call -# 3| 0: [Identifier] puts -# 3| 1: [ArgumentList] ArgumentList -# 3| 0: [String] String -# 3| 0: [ReservedWord] " -# 3| 1: [StringContent] Hello -# 3| 2: [ReservedWord] " -# 4| 5: [ReservedWord] end +# 2| 2: [BodyStatement] BodyStatement +# 2| 0: [SingletonMethod] SingletonMethod +# 2| 0: [ReservedWord] def +# 2| 1: [Self] self +# 2| 2: [ReservedWord] . +# 2| 3: [Identifier] greet +# 3| 4: [BodyStatement] BodyStatement +# 3| 0: [Call] Call +# 3| 0: [Identifier] puts +# 3| 1: [ArgumentList] ArgumentList +# 3| 0: [String] String +# 3| 0: [ReservedWord] " +# 3| 1: [StringContent] Hello +# 3| 2: [ReservedWord] " +# 4| 5: [ReservedWord] end # 5| 3: [ReservedWord] end gems/test.gemspec: # 1| [Program] Program @@ -3641,93 +3688,94 @@ gems/test.gemspec: # 1| 0: [ReservedWord] | # 1| 1: [Identifier] s # 1| 2: [ReservedWord] | -# 2| 2: [Assignment] Assignment -# 2| 0: [Call] Call -# 2| 0: [Identifier] s -# 2| 1: [ReservedWord] . -# 2| 2: [Identifier] name -# 2| 1: [ReservedWord] = -# 2| 2: [String] String -# 2| 0: [ReservedWord] ' -# 2| 1: [StringContent] test -# 2| 2: [ReservedWord] ' -# 3| 3: [Assignment] Assignment -# 3| 0: [Call] Call -# 3| 0: [Identifier] s -# 3| 1: [ReservedWord] . -# 3| 2: [Identifier] version -# 3| 1: [ReservedWord] = -# 3| 2: [String] String -# 3| 0: [ReservedWord] ' -# 3| 1: [StringContent] 0.0.0 -# 3| 2: [ReservedWord] ' -# 4| 4: [Assignment] Assignment -# 4| 0: [Call] Call -# 4| 0: [Identifier] s -# 4| 1: [ReservedWord] . -# 4| 2: [Identifier] summary -# 4| 1: [ReservedWord] = -# 4| 2: [String] String -# 4| 0: [ReservedWord] " -# 4| 1: [StringContent] foo! -# 4| 2: [ReservedWord] " -# 5| 5: [Assignment] Assignment -# 5| 0: [Call] Call -# 5| 0: [Identifier] s -# 5| 1: [ReservedWord] . -# 5| 2: [Identifier] description -# 5| 1: [ReservedWord] = -# 5| 2: [String] String -# 5| 0: [ReservedWord] " -# 5| 1: [StringContent] A test -# 5| 2: [ReservedWord] " -# 6| 6: [Assignment] Assignment -# 6| 0: [Call] Call -# 6| 0: [Identifier] s -# 6| 1: [ReservedWord] . -# 6| 2: [Identifier] authors -# 6| 1: [ReservedWord] = -# 6| 2: [Array] Array -# 6| 0: [ReservedWord] [ -# 6| 1: [String] String -# 6| 0: [ReservedWord] " -# 6| 1: [StringContent] Mona Lisa -# 6| 2: [ReservedWord] " -# 6| 2: [ReservedWord] ] -# 7| 7: [Assignment] Assignment -# 7| 0: [Call] Call -# 7| 0: [Identifier] s -# 7| 1: [ReservedWord] . -# 7| 2: [Identifier] email -# 7| 1: [ReservedWord] = -# 7| 2: [String] String -# 7| 0: [ReservedWord] ' -# 7| 1: [StringContent] mona@example.com -# 7| 2: [ReservedWord] ' -# 8| 8: [Assignment] Assignment -# 8| 0: [Call] Call -# 8| 0: [Identifier] s -# 8| 1: [ReservedWord] . -# 8| 2: [Identifier] files -# 8| 1: [ReservedWord] = -# 8| 2: [Array] Array -# 8| 0: [ReservedWord] [ -# 8| 1: [String] String -# 8| 0: [ReservedWord] " -# 8| 1: [StringContent] lib/test.rb -# 8| 2: [ReservedWord] " -# 8| 2: [ReservedWord] ] -# 9| 9: [Assignment] Assignment -# 9| 0: [Call] Call -# 9| 0: [Identifier] s -# 9| 1: [ReservedWord] . -# 9| 2: [Identifier] homepage -# 9| 1: [ReservedWord] = -# 9| 2: [String] String -# 9| 0: [ReservedWord] ' -# 9| 1: [StringContent] https://github.com/github/codeql-ruby -# 9| 2: [ReservedWord] ' -# 10| 10: [ReservedWord] end +# 2| 2: [BodyStatement] BodyStatement +# 2| 0: [Assignment] Assignment +# 2| 0: [Call] Call +# 2| 0: [Identifier] s +# 2| 1: [ReservedWord] . +# 2| 2: [Identifier] name +# 2| 1: [ReservedWord] = +# 2| 2: [String] String +# 2| 0: [ReservedWord] ' +# 2| 1: [StringContent] test +# 2| 2: [ReservedWord] ' +# 3| 1: [Assignment] Assignment +# 3| 0: [Call] Call +# 3| 0: [Identifier] s +# 3| 1: [ReservedWord] . +# 3| 2: [Identifier] version +# 3| 1: [ReservedWord] = +# 3| 2: [String] String +# 3| 0: [ReservedWord] ' +# 3| 1: [StringContent] 0.0.0 +# 3| 2: [ReservedWord] ' +# 4| 2: [Assignment] Assignment +# 4| 0: [Call] Call +# 4| 0: [Identifier] s +# 4| 1: [ReservedWord] . +# 4| 2: [Identifier] summary +# 4| 1: [ReservedWord] = +# 4| 2: [String] String +# 4| 0: [ReservedWord] " +# 4| 1: [StringContent] foo! +# 4| 2: [ReservedWord] " +# 5| 3: [Assignment] Assignment +# 5| 0: [Call] Call +# 5| 0: [Identifier] s +# 5| 1: [ReservedWord] . +# 5| 2: [Identifier] description +# 5| 1: [ReservedWord] = +# 5| 2: [String] String +# 5| 0: [ReservedWord] " +# 5| 1: [StringContent] A test +# 5| 2: [ReservedWord] " +# 6| 4: [Assignment] Assignment +# 6| 0: [Call] Call +# 6| 0: [Identifier] s +# 6| 1: [ReservedWord] . +# 6| 2: [Identifier] authors +# 6| 1: [ReservedWord] = +# 6| 2: [Array] Array +# 6| 0: [ReservedWord] [ +# 6| 1: [String] String +# 6| 0: [ReservedWord] " +# 6| 1: [StringContent] Mona Lisa +# 6| 2: [ReservedWord] " +# 6| 2: [ReservedWord] ] +# 7| 5: [Assignment] Assignment +# 7| 0: [Call] Call +# 7| 0: [Identifier] s +# 7| 1: [ReservedWord] . +# 7| 2: [Identifier] email +# 7| 1: [ReservedWord] = +# 7| 2: [String] String +# 7| 0: [ReservedWord] ' +# 7| 1: [StringContent] mona@example.com +# 7| 2: [ReservedWord] ' +# 8| 6: [Assignment] Assignment +# 8| 0: [Call] Call +# 8| 0: [Identifier] s +# 8| 1: [ReservedWord] . +# 8| 2: [Identifier] files +# 8| 1: [ReservedWord] = +# 8| 2: [Array] Array +# 8| 0: [ReservedWord] [ +# 8| 1: [String] String +# 8| 0: [ReservedWord] " +# 8| 1: [StringContent] lib/test.rb +# 8| 2: [ReservedWord] " +# 8| 2: [ReservedWord] ] +# 9| 7: [Assignment] Assignment +# 9| 0: [Call] Call +# 9| 0: [Identifier] s +# 9| 1: [ReservedWord] . +# 9| 2: [Identifier] homepage +# 9| 1: [ReservedWord] = +# 9| 2: [String] String +# 9| 0: [ReservedWord] ' +# 9| 1: [StringContent] https://github.com/github/codeql-ruby +# 9| 2: [ReservedWord] ' +# 10| 3: [ReservedWord] end literals/literals.rb: # 1| [Program] Program # 2| 0: [Nil] nil @@ -4444,10 +4492,11 @@ literals/literals.rb: # 174| 127: [Method] Method # 174| 0: [ReservedWord] def # 174| 1: [Identifier] m -# 175| 2: [Assignment] Assignment -# 175| 0: [Identifier] query -# 175| 1: [ReservedWord] = -# 175| 2: [HeredocBeginning] <<-BLA +# 175| 2: [BodyStatement] BodyStatement +# 175| 0: [Assignment] Assignment +# 175| 0: [Identifier] query +# 175| 1: [ReservedWord] = +# 175| 2: [HeredocBeginning] <<-BLA # 178| 3: [ReservedWord] end # 180| 128: [Assignment] Assignment # 180| 0: [Identifier] query @@ -4490,6 +4539,18 @@ literals/literals.rb: # 199| 0: [HashKeySymbol] Z # 199| 1: [ReservedWord] : # 199| 4: [ReservedWord] } +# 201| 135: [String] String +# 201| 0: [ReservedWord] " +# 201| 1: [StringContent] @foo: +# 201| 2: [Interpolation] Interpolation +# 201| 0: [InstanceVariable] @foo +# 201| 3: [StringContent] @@bar: +# 201| 4: [Interpolation] Interpolation +# 201| 0: [ClassVariable] @@bar +# 201| 5: [StringContent] $_: +# 201| 6: [Interpolation] Interpolation +# 201| 0: [GlobalVariable] $_ +# 201| 7: [ReservedWord] " # 1| [Comment] # boolean values and nil # 9| [Comment] # decimal integers # 14| [Comment] # max value representable by QL's int type @@ -4591,14 +4652,9 @@ literals/literals.rb: # 188| [Comment] # TODO: the parser currently does not handle single quoted heredocs correctly # 189| [HeredocBody] HeredocBody # 189| 0: [HeredocContent] -# 189| text without -# 190| 1: [Interpolation] Interpolation -# 190| 0: [ReservedWord] #{ -# 190| 1: [Identifier] interpolation -# 190| 2: [ReservedWord] } -# 190| 2: [HeredocContent] ! -# 190| -# 191| 3: [HeredocEnd] DOC +# 189| text without #{ interpolation } ! +# 189| +# 191| 1: [HeredocEnd] DOC # 193| [HeredocBody] HeredocBody # 193| 0: [HeredocContent] # 193| cat file.txt @@ -4740,46 +4796,49 @@ modules/classes.rb: # 20| 5: [Class] Class # 20| 0: [ReservedWord] class # 20| 1: [Constant] Wibble -# 21| 2: [Method] Method -# 21| 0: [ReservedWord] def -# 21| 1: [Identifier] method_a -# 22| 2: [Call] Call -# 22| 0: [Identifier] puts -# 22| 1: [ArgumentList] ArgumentList -# 22| 0: [String] String -# 22| 0: [ReservedWord] ' -# 22| 1: [StringContent] a -# 22| 2: [ReservedWord] ' -# 23| 3: [ReservedWord] end -# 25| 3: [Method] Method -# 25| 0: [ReservedWord] def -# 25| 1: [Identifier] method_b -# 26| 2: [Call] Call -# 26| 0: [Identifier] puts -# 26| 1: [ArgumentList] ArgumentList -# 26| 0: [String] String -# 26| 0: [ReservedWord] ' -# 26| 1: [StringContent] b -# 26| 2: [ReservedWord] ' -# 27| 3: [ReservedWord] end -# 29| 4: [Call] Call -# 29| 0: [Identifier] some_method_call -# 29| 1: [ArgumentList] ArgumentList -# 29| 0: [ReservedWord] ( -# 29| 1: [ReservedWord] ) -# 30| 5: [Assignment] Assignment -# 30| 0: [GlobalVariable] $global_var -# 30| 1: [ReservedWord] = -# 30| 2: [Integer] 123 -# 32| 6: [Class] Class -# 32| 0: [ReservedWord] class -# 32| 1: [Constant] ClassInWibble -# 33| 2: [ReservedWord] end -# 35| 7: [Module] Module -# 35| 0: [ReservedWord] module -# 35| 1: [Constant] ModuleInWibble -# 36| 2: [ReservedWord] end -# 37| 8: [ReservedWord] end +# 21| 2: [BodyStatement] BodyStatement +# 21| 0: [Method] Method +# 21| 0: [ReservedWord] def +# 21| 1: [Identifier] method_a +# 22| 2: [BodyStatement] BodyStatement +# 22| 0: [Call] Call +# 22| 0: [Identifier] puts +# 22| 1: [ArgumentList] ArgumentList +# 22| 0: [String] String +# 22| 0: [ReservedWord] ' +# 22| 1: [StringContent] a +# 22| 2: [ReservedWord] ' +# 23| 3: [ReservedWord] end +# 25| 1: [Method] Method +# 25| 0: [ReservedWord] def +# 25| 1: [Identifier] method_b +# 26| 2: [BodyStatement] BodyStatement +# 26| 0: [Call] Call +# 26| 0: [Identifier] puts +# 26| 1: [ArgumentList] ArgumentList +# 26| 0: [String] String +# 26| 0: [ReservedWord] ' +# 26| 1: [StringContent] b +# 26| 2: [ReservedWord] ' +# 27| 3: [ReservedWord] end +# 29| 2: [Call] Call +# 29| 0: [Identifier] some_method_call +# 29| 1: [ArgumentList] ArgumentList +# 29| 0: [ReservedWord] ( +# 29| 1: [ReservedWord] ) +# 30| 3: [Assignment] Assignment +# 30| 0: [GlobalVariable] $global_var +# 30| 1: [ReservedWord] = +# 30| 2: [Integer] 123 +# 32| 4: [Class] Class +# 32| 0: [ReservedWord] class +# 32| 1: [Constant] ClassInWibble +# 33| 2: [ReservedWord] end +# 35| 5: [Module] Module +# 35| 0: [ReservedWord] module +# 35| 1: [Constant] ModuleInWibble +# 36| 2: [ReservedWord] end +# 37| 3: [ReservedWord] end # 40| 6: [Assignment] Assignment # 40| 0: [Identifier] x # 40| 1: [ReservedWord] = @@ -4791,31 +4850,34 @@ modules/classes.rb: # 41| 0: [ReservedWord] class # 41| 1: [ReservedWord] << # 41| 2: [Identifier] x -# 42| 3: [Method] Method -# 42| 0: [ReservedWord] def -# 42| 1: [Identifier] length -# 43| 2: [Binary] Binary -# 43| 0: [Integer] 100 -# 43| 1: [ReservedWord] * -# 43| 2: [Super] super -# 44| 3: [ReservedWord] end -# 46| 4: [Method] Method -# 46| 0: [ReservedWord] def -# 46| 1: [Identifier] wibble -# 47| 2: [Call] Call -# 47| 0: [Identifier] puts -# 47| 1: [ArgumentList] ArgumentList -# 47| 0: [String] String -# 47| 0: [ReservedWord] ' -# 47| 1: [StringContent] wibble -# 47| 2: [ReservedWord] ' -# 48| 3: [ReservedWord] end -# 50| 5: [Identifier] another_method_call -# 51| 6: [Assignment] Assignment -# 51| 0: [GlobalVariable] $global_var2 -# 51| 1: [ReservedWord] = -# 51| 2: [Integer] 456 -# 52| 7: [ReservedWord] end +# 42| 3: [BodyStatement] BodyStatement +# 42| 0: [Method] Method +# 42| 0: [ReservedWord] def +# 42| 1: [Identifier] length +# 43| 2: [BodyStatement] BodyStatement +# 43| 0: [Binary] Binary +# 43| 0: [Integer] 100 +# 43| 1: [ReservedWord] * +# 43| 2: [Super] super +# 44| 3: [ReservedWord] end +# 46| 1: [Method] Method +# 46| 0: [ReservedWord] def +# 46| 1: [Identifier] wibble +# 47| 2: [BodyStatement] BodyStatement +# 47| 0: [Call] Call +# 47| 0: [Identifier] puts +# 47| 1: [ArgumentList] ArgumentList +# 47| 0: [String] String +# 47| 0: [ReservedWord] ' +# 47| 1: [StringContent] wibble +# 47| 2: [ReservedWord] ' +# 48| 3: [ReservedWord] end +# 50| 2: [Identifier] another_method_call +# 51| 3: [Assignment] Assignment +# 51| 0: [GlobalVariable] $global_var2 +# 51| 1: [ReservedWord] = +# 51| 2: [Integer] 456 +# 52| 4: [ReservedWord] end # 55| 8: [Class] Class # 55| 0: [ReservedWord] class # 55| 1: [ScopeResolution] ScopeResolution @@ -4838,121 +4900,126 @@ modules/modules.rb: # 4| 1: [Module] Module # 4| 0: [ReservedWord] module # 4| 1: [Constant] Foo -# 5| 2: [Module] Module -# 5| 0: [ReservedWord] module -# 5| 1: [Constant] Bar -# 6| 2: [Class] Class -# 6| 0: [ReservedWord] class -# 6| 1: [Constant] ClassInFooBar -# 7| 2: [ReservedWord] end -# 9| 3: [Method] Method -# 9| 0: [ReservedWord] def -# 9| 1: [Identifier] method_in_foo_bar -# 10| 2: [ReservedWord] end -# 12| 4: [Call] Call -# 12| 0: [Identifier] puts -# 12| 1: [ArgumentList] ArgumentList -# 12| 0: [String] String -# 12| 0: [ReservedWord] ' -# 12| 1: [StringContent] module Foo::Bar -# 12| 2: [ReservedWord] ' -# 13| 5: [Assignment] Assignment -# 13| 0: [GlobalVariable] $global_var -# 13| 1: [ReservedWord] = -# 13| 2: [Integer] 0 -# 14| 6: [ReservedWord] end -# 16| 3: [Method] Method -# 16| 0: [ReservedWord] def -# 16| 1: [Identifier] method_in_foo -# 17| 2: [ReservedWord] end -# 19| 4: [Class] Class -# 19| 0: [ReservedWord] class -# 19| 1: [Constant] ClassInFoo -# 20| 2: [ReservedWord] end -# 22| 5: [Call] Call -# 22| 0: [Identifier] puts -# 22| 1: [ArgumentList] ArgumentList -# 22| 0: [String] String -# 22| 0: [ReservedWord] ' -# 22| 1: [StringContent] module Foo -# 22| 2: [ReservedWord] ' -# 23| 6: [Assignment] Assignment -# 23| 0: [GlobalVariable] $global_var -# 23| 1: [ReservedWord] = -# 23| 2: [Integer] 1 -# 24| 7: [ReservedWord] end +# 5| 2: [BodyStatement] BodyStatement +# 5| 0: [Module] Module +# 5| 0: [ReservedWord] module +# 5| 1: [Constant] Bar +# 6| 2: [BodyStatement] BodyStatement +# 6| 0: [Class] Class +# 6| 0: [ReservedWord] class +# 6| 1: [Constant] ClassInFooBar +# 7| 2: [ReservedWord] end +# 9| 1: [Method] Method +# 9| 0: [ReservedWord] def +# 9| 1: [Identifier] method_in_foo_bar +# 10| 2: [ReservedWord] end +# 12| 2: [Call] Call +# 12| 0: [Identifier] puts +# 12| 1: [ArgumentList] ArgumentList +# 12| 0: [String] String +# 12| 0: [ReservedWord] ' +# 12| 1: [StringContent] module Foo::Bar +# 12| 2: [ReservedWord] ' +# 13| 3: [Assignment] Assignment +# 13| 0: [GlobalVariable] $global_var +# 13| 1: [ReservedWord] = +# 13| 2: [Integer] 0 +# 14| 3: [ReservedWord] end +# 16| 1: [Method] Method +# 16| 0: [ReservedWord] def +# 16| 1: [Identifier] method_in_foo +# 17| 2: [ReservedWord] end +# 19| 2: [Class] Class +# 19| 0: [ReservedWord] class +# 19| 1: [Constant] ClassInFoo +# 20| 2: [ReservedWord] end +# 22| 3: [Call] Call +# 22| 0: [Identifier] puts +# 22| 1: [ArgumentList] ArgumentList +# 22| 0: [String] String +# 22| 0: [ReservedWord] ' +# 22| 1: [StringContent] module Foo +# 22| 2: [ReservedWord] ' +# 23| 4: [Assignment] Assignment +# 23| 0: [GlobalVariable] $global_var +# 23| 1: [ReservedWord] = +# 23| 2: [Integer] 1 +# 24| 3: [ReservedWord] end # 26| 2: [Module] Module # 26| 0: [ReservedWord] module # 26| 1: [Constant] Foo -# 27| 2: [Method] Method -# 27| 0: [ReservedWord] def -# 27| 1: [Identifier] method_in_another_definition_of_foo -# 28| 2: [ReservedWord] end -# 30| 3: [Class] Class -# 30| 0: [ReservedWord] class -# 30| 1: [Constant] ClassInAnotherDefinitionOfFoo -# 31| 2: [ReservedWord] end -# 33| 4: [Call] Call -# 33| 0: [Identifier] puts -# 33| 1: [ArgumentList] ArgumentList -# 33| 0: [String] String -# 33| 0: [ReservedWord] ' -# 33| 1: [StringContent] module Foo again -# 33| 2: [ReservedWord] ' -# 34| 5: [Assignment] Assignment -# 34| 0: [GlobalVariable] $global_var -# 34| 1: [ReservedWord] = -# 34| 2: [Integer] 2 -# 35| 6: [ReservedWord] end +# 27| 2: [BodyStatement] BodyStatement +# 27| 0: [Method] Method +# 27| 0: [ReservedWord] def +# 27| 1: [Identifier] method_in_another_definition_of_foo +# 28| 2: [ReservedWord] end +# 30| 1: [Class] Class +# 30| 0: [ReservedWord] class +# 30| 1: [Constant] ClassInAnotherDefinitionOfFoo +# 31| 2: [ReservedWord] end +# 33| 2: [Call] Call +# 33| 0: [Identifier] puts +# 33| 1: [ArgumentList] ArgumentList +# 33| 0: [String] String +# 33| 0: [ReservedWord] ' +# 33| 1: [StringContent] module Foo again +# 33| 2: [ReservedWord] ' +# 34| 3: [Assignment] Assignment +# 34| 0: [GlobalVariable] $global_var +# 34| 1: [ReservedWord] = +# 34| 2: [Integer] 2 +# 35| 3: [ReservedWord] end # 37| 3: [Module] Module # 37| 0: [ReservedWord] module # 37| 1: [Constant] Bar -# 38| 2: [Method] Method -# 38| 0: [ReservedWord] def -# 38| 1: [Identifier] method_a -# 39| 2: [ReservedWord] end -# 41| 3: [Method] Method -# 41| 0: [ReservedWord] def -# 41| 1: [Identifier] method_b -# 42| 2: [ReservedWord] end -# 44| 4: [Call] Call -# 44| 0: [Identifier] puts -# 44| 1: [ArgumentList] ArgumentList -# 44| 0: [String] String -# 44| 0: [ReservedWord] ' -# 44| 1: [StringContent] module Bar -# 44| 2: [ReservedWord] ' -# 45| 5: [Assignment] Assignment -# 45| 0: [GlobalVariable] $global_var -# 45| 1: [ReservedWord] = -# 45| 2: [Integer] 3 -# 46| 6: [ReservedWord] end +# 38| 2: [BodyStatement] BodyStatement +# 38| 0: [Method] Method +# 38| 0: [ReservedWord] def +# 38| 1: [Identifier] method_a +# 39| 2: [ReservedWord] end +# 41| 1: [Method] Method +# 41| 0: [ReservedWord] def +# 41| 1: [Identifier] method_b +# 42| 2: [ReservedWord] end +# 44| 2: [Call] Call +# 44| 0: [Identifier] puts +# 44| 1: [ArgumentList] ArgumentList +# 44| 0: [String] String +# 44| 0: [ReservedWord] ' +# 44| 1: [StringContent] module Bar +# 44| 2: [ReservedWord] ' +# 45| 3: [Assignment] Assignment +# 45| 0: [GlobalVariable] $global_var +# 45| 1: [ReservedWord] = +# 45| 2: [Integer] 3 +# 46| 3: [ReservedWord] end # 48| 4: [Module] Module # 48| 0: [ReservedWord] module # 48| 1: [ScopeResolution] ScopeResolution # 48| 0: [Constant] Foo # 48| 1: [ReservedWord] :: # 48| 2: [Constant] Bar -# 49| 2: [Class] Class -# 49| 0: [ReservedWord] class -# 49| 1: [Constant] ClassInAnotherDefinitionOfFooBar -# 50| 2: [ReservedWord] end -# 52| 3: [Method] Method -# 52| 0: [ReservedWord] def -# 52| 1: [Identifier] method_in_another_definition_of_foo_bar -# 53| 2: [ReservedWord] end -# 55| 4: [Call] Call -# 55| 0: [Identifier] puts -# 55| 1: [ArgumentList] ArgumentList -# 55| 0: [String] String -# 55| 0: [ReservedWord] ' -# 55| 1: [StringContent] module Foo::Bar again -# 55| 2: [ReservedWord] ' -# 56| 5: [Assignment] Assignment -# 56| 0: [GlobalVariable] $global_var -# 56| 1: [ReservedWord] = -# 56| 2: [Integer] 4 -# 57| 6: [ReservedWord] end +# 49| 2: [BodyStatement] BodyStatement +# 49| 0: [Class] Class +# 49| 0: [ReservedWord] class +# 49| 1: [Constant] ClassInAnotherDefinitionOfFooBar +# 50| 2: [ReservedWord] end +# 52| 1: [Method] Method +# 52| 0: [ReservedWord] def +# 52| 1: [Identifier] method_in_another_definition_of_foo_bar +# 53| 2: [ReservedWord] end +# 55| 2: [Call] Call +# 55| 0: [Identifier] puts +# 55| 1: [ArgumentList] ArgumentList +# 55| 0: [String] String +# 55| 0: [ReservedWord] ' +# 55| 1: [StringContent] module Foo::Bar again +# 55| 2: [ReservedWord] ' +# 56| 3: [Assignment] Assignment +# 56| 0: [GlobalVariable] $global_var +# 56| 1: [ReservedWord] = +# 56| 2: [Integer] 4 +# 57| 3: [ReservedWord] end # 60| 5: [Module] Module # 60| 0: [ReservedWord] module # 60| 1: [ScopeResolution] ScopeResolution @@ -4962,126 +5029,136 @@ modules/modules.rb: # 63| 6: [Module] Module # 63| 0: [ReservedWord] module # 63| 1: [Constant] Test -# 65| 2: [Module] Module -# 65| 0: [ReservedWord] module -# 65| 1: [Constant] Foo1 -# 66| 2: [Class] Class -# 66| 0: [ReservedWord] class -# 66| 1: [ScopeResolution] ScopeResolution -# 66| 0: [Constant] Foo1 -# 66| 1: [ReservedWord] :: -# 66| 2: [Constant] Bar -# 67| 2: [ReservedWord] end -# 68| 3: [ReservedWord] end -# 70| 3: [Module] Module -# 70| 0: [ReservedWord] module -# 70| 1: [Constant] Foo2 -# 71| 2: [Module] Module -# 71| 0: [ReservedWord] module -# 71| 1: [Constant] Foo2 -# 71| 2: [ReservedWord] end -# 72| 3: [Class] Class -# 72| 0: [ReservedWord] class -# 72| 1: [ScopeResolution] ScopeResolution -# 72| 0: [Constant] Foo2 -# 72| 1: [ReservedWord] :: -# 72| 2: [Constant] Bar -# 73| 2: [ReservedWord] end -# 74| 4: [ReservedWord] end -# 76| 4: [Module] Module -# 76| 0: [ReservedWord] module -# 76| 1: [Constant] Foo3 -# 77| 2: [Assignment] Assignment -# 77| 0: [Constant] Foo3 -# 77| 1: [ReservedWord] = -# 77| 2: [Constant] Object -# 78| 3: [Class] Class -# 78| 0: [ReservedWord] class -# 78| 1: [ScopeResolution] ScopeResolution -# 78| 0: [Constant] Foo3 -# 78| 1: [ReservedWord] :: -# 78| 2: [Constant] Bar -# 79| 2: [ReservedWord] end -# 80| 4: [ReservedWord] end -# 81| 5: [ReservedWord] end +# 65| 2: [BodyStatement] BodyStatement +# 65| 0: [Module] Module +# 65| 0: [ReservedWord] module +# 65| 1: [Constant] Foo1 +# 66| 2: [BodyStatement] BodyStatement +# 66| 0: [Class] Class +# 66| 0: [ReservedWord] class +# 66| 1: [ScopeResolution] ScopeResolution +# 66| 0: [Constant] Foo1 +# 66| 1: [ReservedWord] :: +# 66| 2: [Constant] Bar +# 67| 2: [ReservedWord] end +# 68| 3: [ReservedWord] end +# 70| 1: [Module] Module +# 70| 0: [ReservedWord] module +# 70| 1: [Constant] Foo2 +# 71| 2: [BodyStatement] BodyStatement +# 71| 0: [Module] Module +# 71| 0: [ReservedWord] module +# 71| 1: [Constant] Foo2 +# 71| 2: [ReservedWord] end +# 72| 1: [Class] Class +# 72| 0: [ReservedWord] class +# 72| 1: [ScopeResolution] ScopeResolution +# 72| 0: [Constant] Foo2 +# 72| 1: [ReservedWord] :: +# 72| 2: [Constant] Bar +# 73| 2: [ReservedWord] end +# 74| 3: [ReservedWord] end +# 76| 2: [Module] Module +# 76| 0: [ReservedWord] module +# 76| 1: [Constant] Foo3 +# 77| 2: [BodyStatement] BodyStatement +# 77| 0: [Assignment] Assignment +# 77| 0: [Constant] Foo3 +# 77| 1: [ReservedWord] = +# 77| 2: [Constant] Object +# 78| 1: [Class] Class +# 78| 0: [ReservedWord] class +# 78| 1: [ScopeResolution] ScopeResolution +# 78| 0: [Constant] Foo3 +# 78| 1: [ReservedWord] :: +# 78| 2: [Constant] Bar +# 79| 2: [ReservedWord] end +# 80| 3: [ReservedWord] end +# 81| 3: [ReservedWord] end # 83| 7: [Module] Module # 83| 0: [ReservedWord] module # 83| 1: [Constant] Other -# 84| 2: [Module] Module -# 84| 0: [ReservedWord] module -# 84| 1: [Constant] Foo1 -# 85| 2: [ReservedWord] end +# 84| 2: [BodyStatement] BodyStatement +# 84| 0: [Module] Module +# 84| 0: [ReservedWord] module +# 84| 1: [Constant] Foo1 +# 85| 2: [ReservedWord] end # 86| 3: [ReservedWord] end # 88| 8: [Module] Module # 88| 0: [ReservedWord] module # 88| 1: [Constant] IncludeTest -# 89| 2: [Call] Call -# 89| 0: [Identifier] include -# 89| 1: [ArgumentList] ArgumentList -# 89| 0: [ScopeResolution] ScopeResolution -# 89| 0: [ReservedWord] :: -# 89| 1: [Constant] Test -# 90| 3: [Call] Call -# 90| 0: [Constant] Object -# 90| 1: [ReservedWord] . -# 90| 2: [Identifier] module_eval -# 90| 3: [Block] Block -# 90| 0: [ReservedWord] { -# 90| 1: [Call] Call -# 90| 0: [Identifier] prepend -# 90| 1: [ArgumentList] ArgumentList -# 90| 0: [Constant] Other -# 90| 2: [ReservedWord] } -# 91| 4: [Module] Module -# 91| 0: [ReservedWord] module -# 91| 1: [ScopeResolution] ScopeResolution -# 91| 0: [Constant] Foo1 -# 91| 1: [ReservedWord] :: -# 91| 2: [Constant] Y -# 92| 2: [ReservedWord] end -# 93| 5: [ReservedWord] end +# 89| 2: [BodyStatement] BodyStatement +# 89| 0: [Call] Call +# 89| 0: [Identifier] include +# 89| 1: [ArgumentList] ArgumentList +# 89| 0: [ScopeResolution] ScopeResolution +# 89| 0: [ReservedWord] :: +# 89| 1: [Constant] Test +# 90| 1: [Call] Call +# 90| 0: [Constant] Object +# 90| 1: [ReservedWord] . +# 90| 2: [Identifier] module_eval +# 90| 3: [Block] Block +# 90| 0: [ReservedWord] { +# 90| 1: [BlockBody] BlockBody +# 90| 0: [Call] Call +# 90| 0: [Identifier] prepend +# 90| 1: [ArgumentList] ArgumentList +# 90| 0: [Constant] Other +# 90| 2: [ReservedWord] } +# 91| 2: [Module] Module +# 91| 0: [ReservedWord] module +# 91| 1: [ScopeResolution] ScopeResolution +# 91| 0: [Constant] Foo1 +# 91| 1: [ReservedWord] :: +# 91| 2: [Constant] Y +# 92| 2: [ReservedWord] end +# 93| 3: [ReservedWord] end # 95| 9: [Module] Module # 95| 0: [ReservedWord] module # 95| 1: [Constant] IncludeTest2 -# 96| 2: [Call] Call -# 96| 0: [Identifier] include -# 96| 1: [ArgumentList] ArgumentList -# 96| 0: [Constant] Test -# 97| 3: [Module] Module -# 97| 0: [ReservedWord] module -# 97| 1: [ScopeResolution] ScopeResolution -# 97| 0: [Constant] Foo1 -# 97| 1: [ReservedWord] :: -# 97| 2: [Constant] Z -# 98| 2: [ReservedWord] end -# 99| 4: [ReservedWord] end +# 96| 2: [BodyStatement] BodyStatement +# 96| 0: [Call] Call +# 96| 0: [Identifier] include +# 96| 1: [ArgumentList] ArgumentList +# 96| 0: [Constant] Test +# 97| 1: [Module] Module +# 97| 0: [ReservedWord] module +# 97| 1: [ScopeResolution] ScopeResolution +# 97| 0: [Constant] Foo1 +# 97| 1: [ReservedWord] :: +# 97| 2: [Constant] Z +# 98| 2: [ReservedWord] end +# 99| 3: [ReservedWord] end # 101| 10: [Module] Module # 101| 0: [ReservedWord] module # 101| 1: [Constant] PrependTest -# 102| 2: [Call] Call -# 102| 0: [Identifier] prepend -# 102| 1: [ArgumentList] ArgumentList -# 102| 0: [ScopeResolution] ScopeResolution -# 102| 0: [ReservedWord] :: -# 102| 1: [Constant] Test -# 103| 3: [Module] Module -# 103| 0: [ReservedWord] module -# 103| 1: [ScopeResolution] ScopeResolution -# 103| 0: [Constant] Foo2 -# 103| 1: [ReservedWord] :: -# 103| 2: [Constant] Y -# 104| 2: [ReservedWord] end -# 105| 4: [ReservedWord] end +# 102| 2: [BodyStatement] BodyStatement +# 102| 0: [Call] Call +# 102| 0: [Identifier] prepend +# 102| 1: [ArgumentList] ArgumentList +# 102| 0: [ScopeResolution] ScopeResolution +# 102| 0: [ReservedWord] :: +# 102| 1: [Constant] Test +# 103| 1: [Module] Module +# 103| 0: [ReservedWord] module +# 103| 1: [ScopeResolution] ScopeResolution +# 103| 0: [Constant] Foo2 +# 103| 1: [ReservedWord] :: +# 103| 2: [Constant] Y +# 104| 2: [ReservedWord] end +# 105| 3: [ReservedWord] end # 107| 11: [Module] Module # 107| 0: [ReservedWord] module # 107| 1: [Constant] MM -# 108| 2: [Module] Module -# 108| 0: [ReservedWord] module -# 108| 1: [ScopeResolution] ScopeResolution -# 108| 0: [Constant] MM -# 108| 1: [ReservedWord] :: -# 108| 2: [Constant] MM -# 109| 2: [ReservedWord] end +# 108| 2: [BodyStatement] BodyStatement +# 108| 0: [Module] Module +# 108| 0: [ReservedWord] module +# 108| 1: [ScopeResolution] ScopeResolution +# 108| 0: [Constant] MM +# 108| 1: [ReservedWord] :: +# 108| 2: [Constant] MM +# 109| 2: [ReservedWord] end # 110| 3: [ReservedWord] end # 112| 12: [Class] Class # 112| 0: [ReservedWord] class @@ -5090,13 +5167,14 @@ modules/modules.rb: # 115| 13: [Module] Module # 115| 0: [ReservedWord] module # 115| 1: [Constant] XX -# 116| 2: [Class] Class -# 116| 0: [ReservedWord] class -# 116| 1: [Constant] YY -# 116| 2: [Superclass] Superclass -# 116| 0: [ReservedWord] < +# 116| 2: [BodyStatement] BodyStatement +# 116| 0: [Class] Class +# 116| 0: [ReservedWord] class # 116| 1: [Constant] YY -# 117| 3: [ReservedWord] end +# 116| 2: [Superclass] Superclass +# 116| 0: [ReservedWord] < +# 116| 1: [Constant] YY +# 117| 3: [ReservedWord] end # 118| 3: [ReservedWord] end # 120| 14: [Module] Module # 120| 0: [ReservedWord] module @@ -5239,37 +5317,38 @@ operations/operations.rb: # 29| 0: [ReservedWord] def # 29| 1: [Identifier] foo # 29| 2: [ReservedWord] ; -# 29| 3: [Return] Return -# 29| 0: [ReservedWord] return -# 29| 1: [ArgumentList] ArgumentList -# 29| 0: [Integer] 1 -# 29| 1: [ReservedWord] , -# 29| 2: [SplatArgument] SplatArgument -# 29| 0: [ReservedWord] * -# 29| 1: [Array] Array -# 29| 0: [ReservedWord] [ -# 29| 1: [Integer] 2 -# 29| 2: [ReservedWord] ] -# 29| 3: [ReservedWord] , -# 29| 4: [Pair] Pair -# 29| 0: [HashKeySymbol] a -# 29| 1: [ReservedWord] : -# 29| 2: [Integer] 3 -# 29| 5: [ReservedWord] , -# 29| 6: [HashSplatArgument] HashSplatArgument -# 29| 0: [ReservedWord] ** -# 29| 1: [Hash] Hash -# 29| 0: [ReservedWord] { -# 29| 1: [Pair] Pair -# 29| 0: [HashKeySymbol] b -# 29| 1: [ReservedWord] : -# 29| 2: [Integer] 4 -# 29| 2: [ReservedWord] , -# 29| 3: [Pair] Pair -# 29| 0: [HashKeySymbol] c -# 29| 1: [ReservedWord] : -# 29| 2: [Integer] 5 -# 29| 4: [ReservedWord] } +# 29| 3: [BodyStatement] BodyStatement +# 29| 0: [Return] Return +# 29| 0: [ReservedWord] return +# 29| 1: [ArgumentList] ArgumentList +# 29| 0: [Integer] 1 +# 29| 1: [ReservedWord] , +# 29| 2: [SplatArgument] SplatArgument +# 29| 0: [ReservedWord] * +# 29| 1: [Array] Array +# 29| 0: [ReservedWord] [ +# 29| 1: [Integer] 2 +# 29| 2: [ReservedWord] ] +# 29| 3: [ReservedWord] , +# 29| 4: [Pair] Pair +# 29| 0: [HashKeySymbol] a +# 29| 1: [ReservedWord] : +# 29| 2: [Integer] 3 +# 29| 5: [ReservedWord] , +# 29| 6: [HashSplatArgument] HashSplatArgument +# 29| 0: [ReservedWord] ** +# 29| 1: [Hash] Hash +# 29| 0: [ReservedWord] { +# 29| 1: [Pair] Pair +# 29| 0: [HashKeySymbol] b +# 29| 1: [ReservedWord] : +# 29| 2: [Integer] 4 +# 29| 2: [ReservedWord] , +# 29| 3: [Pair] Pair +# 29| 0: [HashKeySymbol] c +# 29| 1: [ReservedWord] : +# 29| 2: [Integer] 5 +# 29| 4: [ReservedWord] } # 29| 4: [ReservedWord] end # 32| 25: [Binary] Binary # 32| 0: [Identifier] w @@ -5432,23 +5511,24 @@ operations/operations.rb: # 87| 63: [Class] Class # 87| 0: [ReservedWord] class # 87| 1: [Constant] X -# 88| 2: [Assignment] Assignment -# 88| 0: [InstanceVariable] @x -# 88| 1: [ReservedWord] = -# 88| 2: [Integer] 1 -# 89| 3: [OperatorAssignment] OperatorAssignment -# 89| 0: [InstanceVariable] @x -# 89| 1: [ReservedWord] += -# 89| 2: [Integer] 2 -# 91| 4: [Assignment] Assignment -# 91| 0: [ClassVariable] @@y -# 91| 1: [ReservedWord] = -# 91| 2: [Integer] 3 -# 92| 5: [OperatorAssignment] OperatorAssignment -# 92| 0: [ClassVariable] @@y -# 92| 1: [ReservedWord] /= -# 92| 2: [Integer] 4 -# 93| 6: [ReservedWord] end +# 88| 2: [BodyStatement] BodyStatement +# 88| 0: [Assignment] Assignment +# 88| 0: [InstanceVariable] @x +# 88| 1: [ReservedWord] = +# 88| 2: [Integer] 1 +# 89| 1: [OperatorAssignment] OperatorAssignment +# 89| 0: [InstanceVariable] @x +# 89| 1: [ReservedWord] += +# 89| 2: [Integer] 2 +# 91| 2: [Assignment] Assignment +# 91| 0: [ClassVariable] @@y +# 91| 1: [ReservedWord] = +# 91| 2: [Integer] 3 +# 92| 3: [OperatorAssignment] OperatorAssignment +# 92| 0: [ClassVariable] @@y +# 92| 1: [ReservedWord] /= +# 92| 2: [Integer] 4 +# 93| 3: [ReservedWord] end # 95| 64: [Assignment] Assignment # 95| 0: [GlobalVariable] $global_var # 95| 1: [ReservedWord] = @@ -5501,21 +5581,22 @@ params/params.rb: # 9| 2: [ReservedWord] , # 9| 3: [Identifier] value # 9| 4: [ReservedWord] | -# 10| 2: [Call] Call -# 10| 0: [Identifier] puts -# 10| 1: [ArgumentList] ArgumentList -# 10| 0: [String] String -# 10| 0: [ReservedWord] " -# 10| 1: [Interpolation] Interpolation -# 10| 0: [ReservedWord] #{ -# 10| 1: [Identifier] key -# 10| 2: [ReservedWord] } -# 10| 2: [StringContent] -> -# 10| 3: [Interpolation] Interpolation -# 10| 0: [ReservedWord] #{ -# 10| 1: [Identifier] value -# 10| 2: [ReservedWord] } -# 10| 4: [ReservedWord] " +# 10| 2: [BodyStatement] BodyStatement +# 10| 0: [Call] Call +# 10| 0: [Identifier] puts +# 10| 1: [ArgumentList] ArgumentList +# 10| 0: [String] String +# 10| 0: [ReservedWord] " +# 10| 1: [Interpolation] Interpolation +# 10| 0: [ReservedWord] #{ +# 10| 1: [Identifier] key +# 10| 2: [ReservedWord] } +# 10| 2: [StringContent] -> +# 10| 3: [Interpolation] Interpolation +# 10| 0: [ReservedWord] #{ +# 10| 1: [Identifier] value +# 10| 2: [ReservedWord] } +# 10| 4: [ReservedWord] " # 11| 3: [ReservedWord] end # 14| 3: [Assignment] Assignment # 14| 0: [Identifier] sum @@ -5530,10 +5611,11 @@ params/params.rb: # 14| 4: [ReservedWord] ) # 14| 2: [Block] Block # 14| 0: [ReservedWord] { -# 14| 1: [Binary] Binary -# 14| 0: [Identifier] foo -# 14| 1: [ReservedWord] + -# 14| 2: [Identifier] bar +# 14| 1: [BlockBody] BlockBody +# 14| 0: [Binary] Binary +# 14| 0: [Identifier] foo +# 14| 1: [ReservedWord] + +# 14| 2: [Identifier] bar # 14| 2: [ReservedWord] } # 17| 4: [Method] Method # 17| 0: [ReservedWord] def @@ -5571,13 +5653,14 @@ params/params.rb: # 22| 3: [Identifier] b # 22| 4: [ReservedWord] ) # 22| 2: [ReservedWord] | -# 22| 2: [Call] Call -# 22| 0: [Identifier] puts -# 22| 1: [ArgumentList] ArgumentList -# 22| 0: [Binary] Binary -# 22| 0: [Identifier] a -# 22| 1: [ReservedWord] + -# 22| 2: [Identifier] b +# 22| 2: [BlockBody] BlockBody +# 22| 0: [Call] Call +# 22| 0: [Identifier] puts +# 22| 1: [ArgumentList] ArgumentList +# 22| 0: [Binary] Binary +# 22| 0: [Identifier] a +# 22| 1: [ReservedWord] + +# 22| 2: [Identifier] b # 22| 3: [ReservedWord] } # 25| 7: [Assignment] Assignment # 25| 0: [Identifier] sum_four_values @@ -5602,16 +5685,17 @@ params/params.rb: # 25| 4: [ReservedWord] ) # 25| 2: [Block] Block # 25| 0: [ReservedWord] { -# 26| 1: [Binary] Binary +# 26| 1: [BlockBody] BlockBody # 26| 0: [Binary] Binary # 26| 0: [Binary] Binary -# 26| 0: [Identifier] first +# 26| 0: [Binary] Binary +# 26| 0: [Identifier] first +# 26| 1: [ReservedWord] + +# 26| 2: [Identifier] second # 26| 1: [ReservedWord] + -# 26| 2: [Identifier] second +# 26| 2: [Identifier] third # 26| 1: [ReservedWord] + -# 26| 2: [Identifier] third -# 26| 1: [ReservedWord] + -# 26| 2: [Identifier] fourth +# 26| 2: [Identifier] fourth # 27| 2: [ReservedWord] } # 30| 8: [Method] Method # 30| 0: [ReservedWord] def @@ -5684,13 +5768,14 @@ params/params.rb: # 41| 1: [ReservedWord] : # 41| 2: [Integer] 7 # 41| 6: [ReservedWord] ) -# 42| 3: [Binary] Binary +# 42| 3: [BodyStatement] BodyStatement # 42| 0: [Binary] Binary -# 42| 0: [Identifier] x +# 42| 0: [Binary] Binary +# 42| 0: [Identifier] x +# 42| 1: [ReservedWord] + +# 42| 2: [Identifier] foo # 42| 1: [ReservedWord] + -# 42| 2: [Identifier] foo -# 42| 1: [ReservedWord] + -# 42| 2: [Identifier] bar +# 42| 2: [Identifier] bar # 43| 4: [ReservedWord] end # 46| 12: [Method] Method # 46| 0: [ReservedWord] def @@ -5701,25 +5786,26 @@ params/params.rb: # 46| 0: [ReservedWord] & # 46| 1: [Identifier] block # 46| 2: [ReservedWord] ) -# 47| 3: [Call] Call -# 47| 0: [Identifier] puts -# 47| 1: [ArgumentList] ArgumentList -# 47| 0: [ReservedWord] ( -# 47| 1: [Call] Call -# 47| 0: [Identifier] block -# 47| 1: [ReservedWord] . -# 47| 2: [Identifier] call -# 47| 3: [ArgumentList] ArgumentList -# 47| 0: [Pair] Pair -# 47| 0: [HashKeySymbol] bar -# 47| 1: [ReservedWord] : -# 47| 2: [Integer] 2 -# 47| 1: [ReservedWord] , -# 47| 2: [Pair] Pair -# 47| 0: [HashKeySymbol] foo -# 47| 1: [ReservedWord] : -# 47| 2: [Integer] 3 -# 47| 2: [ReservedWord] ) +# 47| 3: [BodyStatement] BodyStatement +# 47| 0: [Call] Call +# 47| 0: [Identifier] puts +# 47| 1: [ArgumentList] ArgumentList +# 47| 0: [ReservedWord] ( +# 47| 1: [Call] Call +# 47| 0: [Identifier] block +# 47| 1: [ReservedWord] . +# 47| 2: [Identifier] call +# 47| 3: [ArgumentList] ArgumentList +# 47| 0: [Pair] Pair +# 47| 0: [HashKeySymbol] bar +# 47| 1: [ReservedWord] : +# 47| 2: [Integer] 2 +# 47| 1: [ReservedWord] , +# 47| 2: [Pair] Pair +# 47| 0: [HashKeySymbol] foo +# 47| 1: [ReservedWord] : +# 47| 2: [Integer] 3 +# 47| 2: [ReservedWord] ) # 48| 4: [ReservedWord] end # 49| 13: [Call] Call # 49| 0: [Identifier] use_block_with_keyword @@ -5736,10 +5822,11 @@ params/params.rb: # 49| 1: [ReservedWord] : # 49| 2: [Integer] 100 # 49| 4: [ReservedWord] | -# 50| 2: [Binary] Binary -# 50| 0: [Identifier] xx -# 50| 1: [ReservedWord] + -# 50| 2: [Identifier] yy +# 50| 2: [BodyStatement] BodyStatement +# 50| 0: [Binary] Binary +# 50| 0: [Identifier] xx +# 50| 1: [ReservedWord] + +# 50| 2: [Identifier] yy # 51| 3: [ReservedWord] end # 53| 14: [Assignment] Assignment # 53| 0: [Identifier] lambda_with_keyword_params @@ -5761,13 +5848,14 @@ params/params.rb: # 53| 6: [ReservedWord] ) # 53| 2: [Block] Block # 53| 0: [ReservedWord] { -# 54| 1: [Binary] Binary +# 54| 1: [BlockBody] BlockBody # 54| 0: [Binary] Binary -# 54| 0: [Identifier] x +# 54| 0: [Binary] Binary +# 54| 0: [Identifier] x +# 54| 1: [ReservedWord] + +# 54| 2: [Identifier] y # 54| 1: [ReservedWord] + -# 54| 2: [Identifier] y -# 54| 1: [ReservedWord] + -# 54| 2: [Identifier] z +# 54| 2: [Identifier] z # 55| 2: [ReservedWord] } # 58| 15: [Method] Method # 58| 0: [ReservedWord] def @@ -5796,15 +5884,16 @@ params/params.rb: # 62| 0: [ReservedWord] & # 62| 1: [Identifier] block # 62| 2: [ReservedWord] ) -# 63| 3: [Call] Call -# 63| 0: [Identifier] block -# 63| 1: [ReservedWord] . -# 63| 2: [Identifier] call -# 63| 3: [ArgumentList] ArgumentList -# 63| 0: [String] String -# 63| 0: [ReservedWord] ' -# 63| 1: [StringContent] Zeus -# 63| 2: [ReservedWord] ' +# 63| 3: [BodyStatement] BodyStatement +# 63| 0: [Call] Call +# 63| 0: [Identifier] block +# 63| 1: [ReservedWord] . +# 63| 2: [Identifier] call +# 63| 3: [ArgumentList] ArgumentList +# 63| 0: [String] String +# 63| 0: [ReservedWord] ' +# 63| 1: [StringContent] Zeus +# 63| 2: [ReservedWord] ' # 64| 4: [ReservedWord] end # 65| 17: [Call] Call # 65| 0: [Identifier] use_block_with_optional @@ -5819,22 +5908,23 @@ params/params.rb: # 65| 1: [ReservedWord] = # 65| 2: [Integer] 99 # 65| 4: [ReservedWord] | -# 66| 2: [Call] Call -# 66| 0: [Identifier] puts -# 66| 1: [ArgumentList] ArgumentList -# 66| 0: [String] String -# 66| 0: [ReservedWord] " -# 66| 1: [Interpolation] Interpolation -# 66| 0: [ReservedWord] #{ -# 66| 1: [Identifier] name -# 66| 2: [ReservedWord] } -# 66| 2: [StringContent] is -# 66| 3: [Interpolation] Interpolation -# 66| 0: [ReservedWord] #{ -# 66| 1: [Identifier] age -# 66| 2: [ReservedWord] } -# 66| 4: [StringContent] years old -# 66| 5: [ReservedWord] " +# 66| 2: [BodyStatement] BodyStatement +# 66| 0: [Call] Call +# 66| 0: [Identifier] puts +# 66| 1: [ArgumentList] ArgumentList +# 66| 0: [String] String +# 66| 0: [ReservedWord] " +# 66| 1: [Interpolation] Interpolation +# 66| 0: [ReservedWord] #{ +# 66| 1: [Identifier] name +# 66| 2: [ReservedWord] } +# 66| 2: [StringContent] is +# 66| 3: [Interpolation] Interpolation +# 66| 0: [ReservedWord] #{ +# 66| 1: [Identifier] age +# 66| 2: [ReservedWord] } +# 66| 4: [StringContent] years old +# 66| 5: [ReservedWord] " # 67| 3: [ReservedWord] end # 70| 18: [Assignment] Assignment # 70| 0: [Identifier] lambda_with_optional_params @@ -5857,13 +5947,14 @@ params/params.rb: # 70| 6: [ReservedWord] ) # 70| 2: [Block] Block # 70| 0: [ReservedWord] { -# 70| 1: [Binary] Binary +# 70| 1: [BlockBody] BlockBody # 70| 0: [Binary] Binary -# 70| 0: [Identifier] a +# 70| 0: [Binary] Binary +# 70| 0: [Identifier] a +# 70| 1: [ReservedWord] + +# 70| 2: [Identifier] b # 70| 1: [ReservedWord] + -# 70| 2: [Identifier] b -# 70| 1: [ReservedWord] + -# 70| 2: [Identifier] c +# 70| 2: [Identifier] c # 70| 2: [ReservedWord] } # 73| 19: [Method] Method # 73| 0: [ReservedWord] def @@ -5902,23 +5993,24 @@ params/params.rb: # 81| 3: [BlockParameter] BlockParameter # 81| 0: [ReservedWord] & # 81| 4: [ReservedWord] ) -# 82| 3: [Call] Call -# 82| 0: [Identifier] proc -# 82| 1: [ArgumentList] ArgumentList -# 82| 0: [ReservedWord] ( -# 82| 1: [BlockArgument] BlockArgument -# 82| 0: [ReservedWord] & -# 82| 2: [ReservedWord] ) -# 83| 4: [Call] Call -# 83| 0: [Identifier] array -# 83| 1: [ReservedWord] . -# 83| 2: [Identifier] each -# 83| 3: [ArgumentList] ArgumentList -# 83| 0: [ReservedWord] ( -# 83| 1: [BlockArgument] BlockArgument -# 83| 0: [ReservedWord] & -# 83| 2: [ReservedWord] ) -# 84| 5: [ReservedWord] end +# 82| 3: [BodyStatement] BodyStatement +# 82| 0: [Call] Call +# 82| 0: [Identifier] proc +# 82| 1: [ArgumentList] ArgumentList +# 82| 0: [ReservedWord] ( +# 82| 1: [BlockArgument] BlockArgument +# 82| 0: [ReservedWord] & +# 82| 2: [ReservedWord] ) +# 83| 1: [Call] Call +# 83| 0: [Identifier] array +# 83| 1: [ReservedWord] . +# 83| 2: [Identifier] each +# 83| 3: [ArgumentList] ArgumentList +# 83| 0: [ReservedWord] ( +# 83| 1: [BlockArgument] BlockArgument +# 83| 0: [ReservedWord] & +# 83| 2: [ReservedWord] ) +# 84| 4: [ReservedWord] end # 86| 22: [Call] Call # 86| 0: [Identifier] run_block # 86| 1: [Block] Block @@ -5931,10 +6023,11 @@ params/params.rb: # 86| 4: [ReservedWord] , # 86| 5: [Identifier] z # 86| 6: [ReservedWord] | -# 86| 2: [Call] Call -# 86| 0: [Identifier] puts -# 86| 1: [ArgumentList] ArgumentList -# 86| 0: [Identifier] x +# 86| 2: [BlockBody] BlockBody +# 86| 0: [Call] Call +# 86| 0: [Identifier] puts +# 86| 1: [ArgumentList] ArgumentList +# 86| 0: [Identifier] x # 86| 3: [ReservedWord] } # 1| [Comment] # Tests for the different kinds and contexts of parameters. # 3| [Comment] # Method containing identifier parameters diff --git a/ruby/ql/test/library-tests/ast/ValueText.expected b/ruby/ql/test/library-tests/ast/ValueText.expected index e66838fbe2c..1492d619e33 100644 --- a/ruby/ql/test/library-tests/ast/ValueText.expected +++ b/ruby/ql/test/library-tests/ast/ValueText.expected @@ -709,6 +709,7 @@ exprValue | literals/literals.rb:168:9:168:13 | < | -| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | | +| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | \n text without #{ interpolation } !\n | | literals.rb:193:10:193:19 | <<`SCRIPT` | HereDoc | \n cat file.txt\n | | literals.rb:197:5:197:6 | 42 | IntegerLiteral | 42 | | literals.rb:198:1:198:9 | {...} | HashLiteral | | @@ -237,6 +237,7 @@ allLiterals | literals.rb:199:1:199:9 | {...} | HashLiteral | | | literals.rb:199:2:199:2 | :y | SymbolLiteral | :y | | literals.rb:199:7:199:7 | :Z | SymbolLiteral | :Z | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | | stringlikeLiterals | literals.rb:59:1:59:2 | "" | | | literals.rb:60:1:60:2 | "" | | @@ -345,12 +346,13 @@ stringlikeLiterals | literals.rb:175:11:175:16 | <<-BLA | \nsome text\nand some more\n | | literals.rb:180:9:180:19 | <<~SQUIGGLY | \n indented stuff\n | | literals.rb:184:9:184:15 | <<"DOC" | | -| literals.rb:189:9:189:15 | <<'DOC' | | +| literals.rb:189:9:189:15 | <<'DOC' | \n text without #{ interpolation } !\n | | literals.rb:193:10:193:19 | <<`SCRIPT` | \n cat file.txt\n | | literals.rb:198:2:198:2 | :x | :x | | literals.rb:198:6:198:6 | :y | :y | | literals.rb:199:2:199:2 | :y | :y | | literals.rb:199:7:199:7 | :Z | :Z | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | | stringLiterals | literals.rb:59:1:59:2 | "" | | | literals.rb:60:1:60:2 | "" | | @@ -405,6 +407,7 @@ stringLiterals | literals.rb:163:1:163:34 | "abcdefghijklmnopqrstuvwxyzabcdef" | abcdefghijklmnopqrstuvwxyzabcdef | | literals.rb:164:1:164:35 | "foobarfoobarfoobarfoobarfooba..." | foobarfoobarfoobarfoobarfoobarfoo | | literals.rb:165:1:165:40 | "foobar\\\\foobar\\\\foobar\\\\fooba..." | foobar\\foobar\\foobar\\foobar\\foobar | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | | regExpLiterals | literals.rb:149:1:149:2 | // | | | | literals.rb:150:1:150:5 | /foo/ | foo | | @@ -626,14 +629,18 @@ stringComponents | literals.rb:184:9:184:15 | <<"DOC" | HereDoc | 0 | literals.rb:184:16:185:11 | \n text with | StringTextComponent | | literals.rb:184:9:184:15 | <<"DOC" | HereDoc | 1 | literals.rb:185:12:185:29 | #{...} | StringInterpolationComponent | | literals.rb:184:9:184:15 | <<"DOC" | HereDoc | 2 | literals.rb:185:30:185:32 | !\n | StringTextComponent | -| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | 0 | literals.rb:189:16:190:14 | \n text without | StringTextComponent | -| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | 1 | literals.rb:190:15:190:32 | #{...} | StringInterpolationComponent | -| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | 2 | literals.rb:190:33:190:35 | !\n | StringTextComponent | +| literals.rb:189:9:189:15 | <<'DOC' | HereDoc | 0 | literals.rb:189:16:190:35 | \n text without #{ interpolation } !\n | StringTextComponent | | literals.rb:193:10:193:19 | <<`SCRIPT` | HereDoc | 0 | literals.rb:193:20:194:14 | \n cat file.txt\n | StringTextComponent | | literals.rb:198:2:198:2 | :x | SymbolLiteral | 0 | literals.rb:198:2:198:2 | x | StringTextComponent | | literals.rb:198:6:198:6 | :y | SymbolLiteral | 0 | literals.rb:198:6:198:6 | y | StringTextComponent | | literals.rb:199:2:199:2 | :y | SymbolLiteral | 0 | literals.rb:199:2:199:2 | y | StringTextComponent | | literals.rb:199:7:199:7 | :Z | SymbolLiteral | 0 | literals.rb:199:7:199:7 | Z | StringTextComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 0 | literals.rb:201:2:201:7 | @foo: | StringTextComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 1 | literals.rb:201:8:201:12 | #{...} | StringInterpolationComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 2 | literals.rb:201:13:201:20 | @@bar: | StringTextComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 3 | literals.rb:201:21:201:26 | #{...} | StringInterpolationComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 4 | literals.rb:201:27:201:31 | $_: | StringTextComponent | +| literals.rb:201:1:201:35 | "@foo: #{...} @@bar: #{...} $_..." | StringLiteral | 5 | literals.rb:201:32:201:34 | #{...} | StringInterpolationComponent | stringInterpolations | literals.rb:71:10:71:19 | #{...} | 0 | literals.rb:71:13:71:17 | ... + ... | AddExpr | | literals.rb:72:12:72:21 | #{...} | 0 | literals.rb:72:15:72:19 | ... + ... | AddExpr | @@ -659,7 +666,9 @@ stringInterpolations | literals.rb:146:10:146:19 | #{...} | 0 | literals.rb:146:13:146:17 | ... - ... | SubExpr | | literals.rb:171:14:171:22 | #{...} | 0 | literals.rb:171:17:171:20 | call to name | MethodCall | | literals.rb:185:12:185:29 | #{...} | 0 | literals.rb:185:15:185:27 | call to interpolation | MethodCall | -| literals.rb:190:15:190:32 | #{...} | 0 | literals.rb:190:18:190:30 | call to interpolation | MethodCall | +| literals.rb:201:8:201:12 | #{...} | 0 | literals.rb:201:9:201:12 | @foo | InstanceVariableAccess | +| literals.rb:201:21:201:26 | #{...} | 0 | literals.rb:201:22:201:26 | @@bar | ClassVariableAccess | +| literals.rb:201:32:201:34 | #{...} | 0 | literals.rb:201:33:201:34 | $_ | GlobalVariableAccess | concatenatedStrings | literals.rb:75:1:75:17 | "..." "..." | foobarbaz | 0 | literals.rb:75:1:75:5 | "foo" | | literals.rb:75:1:75:17 | "..." "..." | foobarbaz | 1 | literals.rb:75:7:75:11 | "bar" | diff --git a/ruby/ql/test/library-tests/ast/literals/literals.rb b/ruby/ql/test/library-tests/ast/literals/literals.rb index 6ddfc74e88f..e3f13c6bfea 100644 --- a/ruby/ql/test/library-tests/ast/literals/literals.rb +++ b/ruby/ql/test/library-tests/ast/literals/literals.rb @@ -196,4 +196,6 @@ SCRIPT x = 42 {x:, y:5} -{y: , Z:} \ No newline at end of file +{y: , Z:} + +"@foo: #@foo @@bar: #@@bar $_: #$_" \ No newline at end of file diff --git a/ruby/ql/test/library-tests/frameworks/ActionView.ql b/ruby/ql/test/library-tests/frameworks/ActionView.ql index da9381087fd..da5f03467a1 100644 --- a/ruby/ql/test/library-tests/frameworks/ActionView.ql +++ b/ruby/ql/test/library-tests/frameworks/ActionView.ql @@ -13,6 +13,6 @@ query predicate renderToCalls(RenderToCall c) { any() } query predicate linkToCalls(LinkToCall c) { any() } -query predicate httpResponses(HTTP::Server::HttpResponse r, DataFlow::Node body, string mimeType) { +query predicate httpResponses(Http::Server::HttpResponse r, DataFlow::Node body, string mimeType) { r.getBody() = body and r.getMimetype() = mimeType } diff --git a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.ql b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.ql index 17f328ad2ed..0bcc883c364 100644 --- a/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.ql +++ b/ruby/ql/test/library-tests/frameworks/http_clients/HttpClients.ql @@ -2,7 +2,7 @@ import codeql.ruby.Concepts import codeql.ruby.DataFlow query predicate httpRequests( - HTTP::Client::Request r, string framework, DataFlow::Node urlPart, DataFlow::Node responseBody + Http::Client::Request r, string framework, DataFlow::Node urlPart, DataFlow::Node responseBody ) { r.getFramework() = framework and r.getAUrlPart() = urlPart and diff --git a/ruby/ql/test/library-tests/modules/ancestors.expected b/ruby/ql/test/library-tests/modules/ancestors.expected index f371d95a301..ff6d480762a 100644 --- a/ruby/ql/test/library-tests/modules/ancestors.expected +++ b/ruby/ql/test/library-tests/modules/ancestors.expected @@ -155,7 +155,7 @@ modules.rb: # 116| XX::YY #-----| super -> YY -# 120| Test::Foo1::Bar::Baz +# 123| Test::Foo1::Bar::Baz modules_rec.rb: # 1| B::A diff --git a/ruby/ql/test/library-tests/modules/callgraph.expected b/ruby/ql/test/library-tests/modules/callgraph.expected index d40ab5351e5..e07cbdd0779 100644 --- a/ruby/ql/test/library-tests/modules/callgraph.expected +++ b/ruby/ql/test/library-tests/modules/callgraph.expected @@ -79,6 +79,7 @@ getTarget | modules.rb:90:24:90:36 | call to prepend | calls.rb:93:5:93:20 | prepend | | modules.rb:96:3:96:14 | call to include | calls.rb:92:5:92:20 | include | | modules.rb:102:3:102:16 | call to prepend | calls.rb:93:5:93:20 | prepend | +| modules.rb:126:6:126:11 | call to new | calls.rb:99:5:99:16 | new | | modules_rec.rb:8:3:8:11 | call to prepend | calls.rb:93:5:93:20 | prepend | | private.rb:2:3:3:5 | call to private | calls.rb:94:5:94:20 | private | | private.rb:10:3:10:19 | call to private | calls.rb:94:5:94:20 | private | diff --git a/ruby/ql/test/library-tests/modules/methods.expected b/ruby/ql/test/library-tests/modules/methods.expected index a8df6e9ffe8..853e6e2b7ec 100644 --- a/ruby/ql/test/library-tests/modules/methods.expected +++ b/ruby/ql/test/library-tests/modules/methods.expected @@ -272,3 +272,107 @@ lookupMethod | private.rb:42:1:60:3 | F | private3 | private.rb:55:3:56:5 | private3 | | private.rb:42:1:60:3 | F | private4 | private.rb:58:3:59:5 | private4 | | private.rb:42:1:60:3 | F | public | private.rb:46:3:47:5 | public | +enclosingMethod +| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:3:3 | foo | +| calls.rb:2:5:2:14 | self | calls.rb:1:1:3:3 | foo | +| calls.rb:2:10:2:14 | "foo" | calls.rb:1:1:3:3 | foo | +| calls.rb:2:11:2:13 | foo | calls.rb:1:1:3:3 | foo | +| calls.rb:8:5:8:14 | call to puts | calls.rb:7:1:9:3 | bar | +| calls.rb:8:5:8:14 | self | calls.rb:7:1:9:3 | bar | +| calls.rb:8:10:8:14 | "bar" | calls.rb:7:1:9:3 | bar | +| calls.rb:8:11:8:13 | bar | calls.rb:7:1:9:3 | bar | +| calls.rb:38:9:38:18 | call to instance_m | calls.rb:37:5:43:7 | baz | +| calls.rb:38:9:38:18 | self | calls.rb:37:5:43:7 | baz | +| calls.rb:39:9:39:12 | self | calls.rb:37:5:43:7 | baz | +| calls.rb:39:9:39:23 | call to instance_m | calls.rb:37:5:43:7 | baz | +| calls.rb:41:9:41:19 | call to singleton_m | calls.rb:37:5:43:7 | baz | +| calls.rb:41:9:41:19 | self | calls.rb:37:5:43:7 | baz | +| calls.rb:42:9:42:12 | self | calls.rb:37:5:43:7 | baz | +| calls.rb:42:9:42:24 | call to singleton_m | calls.rb:37:5:43:7 | baz | +| calls.rb:53:9:53:13 | call to super | calls.rb:52:5:54:7 | baz | +| calls.rb:62:18:62:18 | a | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:62:18:62:18 | a | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:62:22:62:22 | 4 | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:62:25:62:25 | b | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:62:25:62:25 | b | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:62:28:62:28 | 5 | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:63:5:63:5 | a | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:63:5:63:16 | call to bit_length | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:64:5:64:5 | b | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:64:5:64:16 | call to bit_length | calls.rb:62:1:65:3 | optional_arg | +| calls.rb:68:5:68:11 | yield ... | calls.rb:67:1:69:3 | call_block | +| calls.rb:68:11:68:11 | 1 | calls.rb:67:1:69:3 | call_block | +| calls.rb:72:5:72:7 | var | calls.rb:71:1:75:3 | foo | +| calls.rb:72:5:72:18 | ... = ... | calls.rb:71:1:75:3 | foo | +| calls.rb:72:11:72:14 | Hash | calls.rb:71:1:75:3 | foo | +| calls.rb:72:11:72:18 | call to new | calls.rb:71:1:75:3 | foo | +| calls.rb:73:5:73:7 | var | calls.rb:71:1:75:3 | foo | +| calls.rb:73:5:73:10 | ...[...] | calls.rb:71:1:75:3 | foo | +| calls.rb:73:9:73:9 | 1 | calls.rb:71:1:75:3 | foo | +| calls.rb:74:5:74:29 | call to call_block | calls.rb:71:1:75:3 | foo | +| calls.rb:74:5:74:29 | self | calls.rb:71:1:75:3 | foo | +| calls.rb:74:16:74:29 | { ... } | calls.rb:71:1:75:3 | foo | +| calls.rb:74:19:74:19 | x | calls.rb:71:1:75:3 | foo | +| calls.rb:74:19:74:19 | x | calls.rb:71:1:75:3 | foo | +| calls.rb:74:22:74:24 | var | calls.rb:71:1:75:3 | foo | +| calls.rb:74:22:74:27 | ...[...] | calls.rb:71:1:75:3 | foo | +| calls.rb:74:26:74:26 | x | calls.rb:71:1:75:3 | foo | +| calls.rb:110:15:110:19 | &body | calls.rb:110:3:116:5 | foreach | +| calls.rb:110:16:110:19 | body | calls.rb:110:3:116:5 | foreach | +| calls.rb:111:5:111:5 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:111:5:111:9 | ... = ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:111:9:111:9 | 0 | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:5:115:7 | while ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:11:112:11 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:11:112:25 | ... < ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:15:112:18 | self | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:15:112:25 | call to length | calls.rb:110:3:116:5 | foreach | +| calls.rb:112:26:115:7 | do ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:113:9:113:24 | yield ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:113:15:113:15 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:113:18:113:21 | self | calls.rb:110:3:116:5 | foreach | +| calls.rb:113:18:113:24 | ...[...] | calls.rb:110:3:116:5 | foreach | +| calls.rb:113:23:113:23 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:9:114:9 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:9:114:9 | x | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:9:114:14 | ... += ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:9:114:14 | ... = ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:11:114:12 | ... + ... | calls.rb:110:3:116:5 | foreach | +| calls.rb:114:14:114:14 | 1 | calls.rb:110:3:116:5 | foreach | +| calls.rb:120:5:120:20 | yield ... | calls.rb:119:1:121:3 | funny | +| calls.rb:120:11:120:20 | "prefix: " | calls.rb:119:1:121:3 | funny | +| calls.rb:120:12:120:19 | prefix: | calls.rb:119:1:121:3 | funny | +| calls.rb:137:14:137:15 | &b | calls.rb:137:1:139:3 | indirect | +| calls.rb:137:15:137:15 | b | calls.rb:137:1:139:3 | indirect | +| calls.rb:138:5:138:17 | call to call_block | calls.rb:137:1:139:3 | indirect | +| calls.rb:138:5:138:17 | self | calls.rb:137:1:139:3 | indirect | +| calls.rb:138:16:138:17 | &... | calls.rb:137:1:139:3 | indirect | +| calls.rb:138:17:138:17 | b | calls.rb:137:1:139:3 | indirect | +| calls.rb:146:9:146:12 | self | calls.rb:145:5:147:7 | s_method | +| calls.rb:146:9:146:17 | call to to_s | calls.rb:145:5:147:7 | s_method | +| calls.rb:171:9:171:12 | self | calls.rb:170:5:172:7 | singleton_a | +| calls.rb:171:9:171:24 | call to singleton_b | calls.rb:170:5:172:7 | singleton_a | +| calls.rb:175:9:175:12 | self | calls.rb:174:5:176:7 | singleton_b | +| calls.rb:175:9:175:24 | call to singleton_c | calls.rb:174:5:176:7 | singleton_b | +| calls.rb:182:9:182:12 | self | calls.rb:181:5:183:7 | singleton_d | +| calls.rb:182:9:182:24 | call to singleton_a | calls.rb:181:5:183:7 | singleton_d | +| hello.rb:3:9:3:22 | return | hello.rb:2:5:4:7 | hello | +| hello.rb:3:16:3:22 | "hello" | hello.rb:2:5:4:7 | hello | +| hello.rb:3:17:3:21 | hello | hello.rb:2:5:4:7 | hello | +| hello.rb:6:9:6:22 | return | hello.rb:5:5:7:7 | world | +| hello.rb:6:16:6:22 | "world" | hello.rb:5:5:7:7 | world | +| hello.rb:6:17:6:21 | world | hello.rb:5:5:7:7 | world | +| hello.rb:14:9:14:20 | return | hello.rb:13:5:15:7 | message | +| hello.rb:14:16:14:20 | call to hello | hello.rb:13:5:15:7 | message | +| hello.rb:14:16:14:20 | self | hello.rb:13:5:15:7 | message | +| hello.rb:20:9:20:40 | return | hello.rb:19:5:21:7 | message | +| hello.rb:20:16:20:20 | call to super | hello.rb:19:5:21:7 | message | +| hello.rb:20:16:20:26 | ... + ... | hello.rb:19:5:21:7 | message | +| hello.rb:20:16:20:34 | ... + ... | hello.rb:19:5:21:7 | message | +| hello.rb:20:16:20:40 | ... + ... | hello.rb:19:5:21:7 | message | +| hello.rb:20:24:20:26 | " " | hello.rb:19:5:21:7 | message | +| hello.rb:20:25:20:25 | | hello.rb:19:5:21:7 | message | +| hello.rb:20:30:20:34 | call to world | hello.rb:19:5:21:7 | message | +| hello.rb:20:30:20:34 | self | hello.rb:19:5:21:7 | message | +| hello.rb:20:38:20:40 | "!" | hello.rb:19:5:21:7 | message | +| hello.rb:20:39:20:39 | ! | hello.rb:19:5:21:7 | message | diff --git a/ruby/ql/test/library-tests/modules/methods.ql b/ruby/ql/test/library-tests/modules/methods.ql index e92ce65e684..6442dfb9e51 100644 --- a/ruby/ql/test/library-tests/modules/methods.ql +++ b/ruby/ql/test/library-tests/modules/methods.ql @@ -6,3 +6,5 @@ query MethodBase getMethod(Module m, string name) { } query MethodBase lookupMethod(Module m, string name) { result = M::lookupMethod(m, name) } + +query predicate enclosingMethod(AstNode n, MethodBase m) { m = n.getEnclosingMethod() } diff --git a/ruby/ql/test/library-tests/modules/modules.expected b/ruby/ql/test/library-tests/modules/modules.expected index 29aacf18e5d..01e5685e67e 100644 --- a/ruby/ql/test/library-tests/modules/modules.expected +++ b/ruby/ql/test/library-tests/modules/modules.expected @@ -54,9 +54,9 @@ getModule | modules.rb:107:1:110:3 | MM | | modules.rb:108:3:109:5 | MM::MM | | modules.rb:112:1:113:3 | YY | -| modules.rb:115:1:118:3 | XX | +| modules.rb:115:1:121:3 | XX | | modules.rb:116:7:117:9 | XX::YY | -| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | +| modules.rb:123:1:124:3 | Test::Foo1::Bar::Baz | | modules_rec.rb:1:1:2:3 | B::A | | modules_rec.rb:4:1:5:3 | A::B | | private.rb:1:1:29:3 | E | @@ -72,7 +72,7 @@ getADeclaration | calls.rb:97:1:100:3 | Object | calls.rb:1:1:186:22 | calls.rb | | calls.rb:97:1:100:3 | Object | calls.rb:97:1:100:3 | Object | | calls.rb:97:1:100:3 | Object | hello.rb:1:1:22:3 | hello.rb | -| calls.rb:97:1:100:3 | Object | modules.rb:1:1:121:4 | modules.rb | +| calls.rb:97:1:100:3 | Object | modules.rb:1:1:129:4 | modules.rb | | calls.rb:97:1:100:3 | Object | modules_rec.rb:1:1:11:26 | modules_rec.rb | | calls.rb:97:1:100:3 | Object | private.rb:1:1:60:3 | private.rb | | calls.rb:102:1:104:3 | Hash | calls.rb:102:1:104:3 | Hash | @@ -115,9 +115,9 @@ getADeclaration | modules.rb:107:1:110:3 | MM | modules.rb:107:1:110:3 | MM | | modules.rb:108:3:109:5 | MM::MM | modules.rb:108:3:109:5 | MM | | modules.rb:112:1:113:3 | YY | modules.rb:112:1:113:3 | YY | -| modules.rb:115:1:118:3 | XX | modules.rb:115:1:118:3 | XX | +| modules.rb:115:1:121:3 | XX | modules.rb:115:1:121:3 | XX | | modules.rb:116:7:117:9 | XX::YY | modules.rb:116:7:117:9 | YY | -| modules.rb:120:1:121:3 | Test::Foo1::Bar::Baz | modules.rb:120:1:121:3 | Baz | +| modules.rb:123:1:124:3 | Test::Foo1::Bar::Baz | modules.rb:123:1:124:3 | Baz | | modules_rec.rb:1:1:2:3 | B::A | modules_rec.rb:1:1:2:3 | A | | modules_rec.rb:4:1:5:3 | A::B | modules_rec.rb:4:1:5:3 | B | | private.rb:1:1:29:3 | E | private.rb:1:1:29:3 | E | @@ -205,9 +205,10 @@ resolveConstantReadAccess | modules.rb:103:10:103:13 | Foo2 | Test::Foo2 | | modules.rb:108:10:108:11 | MM | MM | | modules.rb:116:18:116:19 | YY | YY | -| modules.rb:120:8:120:11 | Test | Test | -| modules.rb:120:8:120:17 | Foo1 | Test::Foo1 | -| modules.rb:120:8:120:22 | Bar | Test::Foo1::Bar | +| modules.rb:123:8:123:11 | Test | Test | +| modules.rb:123:8:123:17 | Foo1 | Test::Foo1 | +| modules.rb:123:8:123:22 | Bar | Test::Foo1::Bar | +| modules.rb:126:6:126:7 | XX | XX | | modules_rec.rb:1:7:1:7 | B | B | | modules_rec.rb:4:7:4:7 | A | A | | modules_rec.rb:7:11:7:11 | B | B | @@ -276,11 +277,519 @@ resolveConstantWriteAccess | modules.rb:108:3:109:5 | MM | MM::MM | | modules.rb:108:3:109:5 | MM | MM::MM::MM | | modules.rb:112:1:113:3 | YY | YY | -| modules.rb:115:1:118:3 | XX | XX | +| modules.rb:115:1:121:3 | XX | XX | | modules.rb:116:7:117:9 | YY | XX::YY | -| modules.rb:120:1:121:3 | Baz | Test::Foo1::Bar::Baz | +| modules.rb:123:1:124:3 | Baz | Test::Foo1::Bar::Baz | | modules_rec.rb:1:1:2:3 | A | B::A | | modules_rec.rb:4:1:5:3 | B | A::B | | modules_rec.rb:7:1:9:3 | A | A | | private.rb:1:1:29:3 | E | E | | private.rb:42:1:60:3 | F | F | +enclosingModule +| calls.rb:1:1:3:3 | foo | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:2:5:2:14 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:2:10:2:14 | "foo" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:2:11:2:13 | foo | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:5:1:5:3 | call to foo | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:5:1:5:3 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:7:1:9:3 | bar | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:7:5:7:8 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:8:5:8:14 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:8:5:8:14 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:8:10:8:14 | "bar" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:8:11:8:13 | bar | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:11:1:11:4 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:11:1:11:8 | call to bar | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:13:1:13:4 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:13:1:13:8 | call to foo | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:15:1:24:3 | M | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:16:5:16:23 | instance_m | calls.rb:15:1:24:3 | M | +| calls.rb:17:5:17:29 | singleton_m | calls.rb:15:1:24:3 | M | +| calls.rb:17:9:17:12 | self | calls.rb:15:1:24:3 | M | +| calls.rb:19:5:19:14 | call to instance_m | calls.rb:15:1:24:3 | M | +| calls.rb:19:5:19:14 | self | calls.rb:15:1:24:3 | M | +| calls.rb:20:5:20:8 | self | calls.rb:15:1:24:3 | M | +| calls.rb:20:5:20:19 | call to instance_m | calls.rb:15:1:24:3 | M | +| calls.rb:22:5:22:15 | call to singleton_m | calls.rb:15:1:24:3 | M | +| calls.rb:22:5:22:15 | self | calls.rb:15:1:24:3 | M | +| calls.rb:23:5:23:8 | self | calls.rb:15:1:24:3 | M | +| calls.rb:23:5:23:20 | call to singleton_m | calls.rb:15:1:24:3 | M | +| calls.rb:26:1:26:1 | M | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:26:1:26:12 | call to instance_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:27:1:27:1 | M | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:27:1:27:13 | call to singleton_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:29:1:44:3 | C | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:30:5:30:13 | call to include | calls.rb:29:1:44:3 | C | +| calls.rb:30:5:30:13 | self | calls.rb:29:1:44:3 | C | +| calls.rb:30:13:30:13 | M | calls.rb:29:1:44:3 | C | +| calls.rb:31:5:31:14 | call to instance_m | calls.rb:29:1:44:3 | C | +| calls.rb:31:5:31:14 | self | calls.rb:29:1:44:3 | C | +| calls.rb:32:5:32:8 | self | calls.rb:29:1:44:3 | C | +| calls.rb:32:5:32:19 | call to instance_m | calls.rb:29:1:44:3 | C | +| calls.rb:34:5:34:15 | call to singleton_m | calls.rb:29:1:44:3 | C | +| calls.rb:34:5:34:15 | self | calls.rb:29:1:44:3 | C | +| calls.rb:35:5:35:8 | self | calls.rb:29:1:44:3 | C | +| calls.rb:35:5:35:20 | call to singleton_m | calls.rb:29:1:44:3 | C | +| calls.rb:37:5:43:7 | baz | calls.rb:29:1:44:3 | C | +| calls.rb:38:9:38:18 | call to instance_m | calls.rb:29:1:44:3 | C | +| calls.rb:38:9:38:18 | self | calls.rb:29:1:44:3 | C | +| calls.rb:39:9:39:12 | self | calls.rb:29:1:44:3 | C | +| calls.rb:39:9:39:23 | call to instance_m | calls.rb:29:1:44:3 | C | +| calls.rb:41:9:41:19 | call to singleton_m | calls.rb:29:1:44:3 | C | +| calls.rb:41:9:41:19 | self | calls.rb:29:1:44:3 | C | +| calls.rb:42:9:42:12 | self | calls.rb:29:1:44:3 | C | +| calls.rb:42:9:42:24 | call to singleton_m | calls.rb:29:1:44:3 | C | +| calls.rb:46:1:46:1 | c | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:46:1:46:9 | ... = ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:46:5:46:5 | C | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:46:5:46:9 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:47:1:47:1 | c | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:47:1:47:5 | call to baz | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:48:1:48:1 | c | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:48:1:48:13 | call to singleton_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:49:1:49:1 | c | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:49:1:49:12 | call to instance_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:51:1:55:3 | D | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:51:11:51:11 | C | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:52:5:54:7 | baz | calls.rb:51:1:55:3 | D | +| calls.rb:53:9:53:13 | call to super | calls.rb:51:1:55:3 | D | +| calls.rb:57:1:57:1 | d | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:57:1:57:9 | ... = ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:57:5:57:5 | D | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:57:5:57:9 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:58:1:58:1 | d | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:58:1:58:5 | call to baz | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:59:1:59:1 | d | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:59:1:59:13 | call to singleton_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:60:1:60:1 | d | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:60:1:60:12 | call to instance_m | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:1:65:3 | optional_arg | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:18:62:18 | a | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:18:62:18 | a | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:22:62:22 | 4 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:25:62:25 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:25:62:25 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:62:28:62:28 | 5 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:63:5:63:5 | a | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:63:5:63:16 | call to bit_length | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:64:5:64:5 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:64:5:64:16 | call to bit_length | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:67:1:69:3 | call_block | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:68:5:68:11 | yield ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:68:11:68:11 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:71:1:75:3 | foo | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:72:5:72:7 | var | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:72:5:72:18 | ... = ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:72:11:72:14 | Hash | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:72:11:72:18 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:73:5:73:7 | var | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:73:5:73:10 | ...[...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:73:9:73:9 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:5:74:29 | call to call_block | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:5:74:29 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:16:74:29 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:19:74:19 | x | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:19:74:19 | x | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:22:74:24 | var | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:22:74:27 | ...[...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:74:26:74:26 | x | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:77:1:80:3 | Integer | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:78:5:78:23 | bit_length | calls.rb:77:1:80:3 | Integer | +| calls.rb:79:5:79:16 | abs | calls.rb:77:1:80:3 | Integer | +| calls.rb:82:1:84:3 | String | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:83:5:83:23 | capitalize | calls.rb:82:1:84:3 | String | +| calls.rb:86:1:88:3 | Kernel | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:87:5:87:17 | puts | calls.rb:86:1:88:3 | Kernel | +| calls.rb:90:1:95:3 | Module | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:91:5:91:24 | module_eval | calls.rb:90:1:95:3 | Module | +| calls.rb:92:5:92:20 | include | calls.rb:90:1:95:3 | Module | +| calls.rb:93:5:93:20 | prepend | calls.rb:90:1:95:3 | Module | +| calls.rb:94:5:94:20 | private | calls.rb:90:1:95:3 | Module | +| calls.rb:97:1:100:3 | Object | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:97:16:97:21 | Module | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:98:5:98:18 | call to include | calls.rb:97:1:100:3 | Object | +| calls.rb:98:5:98:18 | self | calls.rb:97:1:100:3 | Object | +| calls.rb:98:13:98:18 | Kernel | calls.rb:97:1:100:3 | Object | +| calls.rb:99:5:99:16 | new | calls.rb:97:1:100:3 | Object | +| calls.rb:102:1:104:3 | Hash | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:103:5:103:15 | [] | calls.rb:102:1:104:3 | Hash | +| calls.rb:106:1:117:3 | Array | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:107:3:107:13 | [] | calls.rb:106:1:117:3 | Array | +| calls.rb:108:3:108:17 | length | calls.rb:106:1:117:3 | Array | +| calls.rb:110:3:116:5 | foreach | calls.rb:106:1:117:3 | Array | +| calls.rb:110:15:110:19 | &body | calls.rb:106:1:117:3 | Array | +| calls.rb:110:16:110:19 | body | calls.rb:106:1:117:3 | Array | +| calls.rb:111:5:111:5 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:111:5:111:9 | ... = ... | calls.rb:106:1:117:3 | Array | +| calls.rb:111:9:111:9 | 0 | calls.rb:106:1:117:3 | Array | +| calls.rb:112:5:115:7 | while ... | calls.rb:106:1:117:3 | Array | +| calls.rb:112:11:112:11 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:112:11:112:25 | ... < ... | calls.rb:106:1:117:3 | Array | +| calls.rb:112:15:112:18 | self | calls.rb:106:1:117:3 | Array | +| calls.rb:112:15:112:25 | call to length | calls.rb:106:1:117:3 | Array | +| calls.rb:112:26:115:7 | do ... | calls.rb:106:1:117:3 | Array | +| calls.rb:113:9:113:24 | yield ... | calls.rb:106:1:117:3 | Array | +| calls.rb:113:15:113:15 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:113:18:113:21 | self | calls.rb:106:1:117:3 | Array | +| calls.rb:113:18:113:24 | ...[...] | calls.rb:106:1:117:3 | Array | +| calls.rb:113:23:113:23 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:114:9:114:9 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:114:9:114:9 | x | calls.rb:106:1:117:3 | Array | +| calls.rb:114:9:114:14 | ... += ... | calls.rb:106:1:117:3 | Array | +| calls.rb:114:9:114:14 | ... = ... | calls.rb:106:1:117:3 | Array | +| calls.rb:114:11:114:12 | ... + ... | calls.rb:106:1:117:3 | Array | +| calls.rb:114:14:114:14 | 1 | calls.rb:106:1:117:3 | Array | +| calls.rb:119:1:121:3 | funny | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:120:5:120:20 | yield ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:120:11:120:20 | "prefix: " | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:120:12:120:19 | prefix: | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:1:123:30 | call to funny | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:1:123:30 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:7:123:30 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:10:123:10 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:10:123:10 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:13:123:29 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:13:123:29 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:18:123:18 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:123:18:123:29 | call to capitalize | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:125:1:125:3 | "a" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:125:1:125:14 | call to capitalize | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:125:2:125:2 | a | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:126:1:126:1 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:126:1:126:12 | call to bit_length | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:127:1:127:1 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:127:1:127:5 | call to abs | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:1:129:13 | Array | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:1:129:13 | [...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:1:129:13 | call to [] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:1:129:62 | call to foreach | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:2:129:4 | "a" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:3:129:3 | a | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:6:129:8 | "b" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:7:129:7 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:10:129:12 | "c" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:11:129:11 | c | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:23:129:62 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:26:129:26 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:26:129:26 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:29:129:29 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:29:129:29 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:32:129:61 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:32:129:61 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:37:129:61 | "#{...} -> #{...}" | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:38:129:41 | #{...} | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:40:129:40 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:42:129:45 | -> | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:46:129:60 | #{...} | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:48:129:48 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:129:48:129:59 | call to capitalize | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:1:131:7 | Array | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:1:131:7 | [...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:1:131:7 | call to [] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:1:131:35 | call to foreach | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:2:131:2 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:4:131:4 | 2 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:6:131:6 | 3 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:17:131:35 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:20:131:20 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:20:131:20 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:23:131:23 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:131:23:131:34 | call to bit_length | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:1:133:7 | Array | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:1:133:7 | [...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:1:133:7 | call to [] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:1:133:40 | call to foreach | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:2:133:2 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:4:133:4 | 2 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:6:133:6 | 3 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:17:133:40 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:20:133:20 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:20:133:20 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:23:133:39 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:23:133:39 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:28:133:28 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:133:28:133:39 | call to capitalize | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:1:135:8 | Array | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:1:135:8 | [...] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:1:135:8 | call to [] | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:1:135:37 | call to foreach | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:2:135:2 | 1 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:4:135:5 | - ... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:5:135:5 | 2 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:7:135:7 | 3 | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:18:135:37 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:21:135:21 | _ | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:21:135:21 | _ | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:24:135:24 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:24:135:24 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:27:135:36 | call to puts | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:27:135:36 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:32:135:32 | v | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:135:32:135:36 | call to abs | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:137:1:139:3 | indirect | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:137:14:137:15 | &b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:137:15:137:15 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:138:5:138:17 | call to call_block | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:138:5:138:17 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:138:16:138:17 | &... | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:138:17:138:17 | b | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:1:141:28 | call to indirect | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:1:141:28 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:10:141:28 | { ... } | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:13:141:13 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:13:141:13 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:16:141:16 | i | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:141:16:141:27 | call to bit_length | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:144:1:148:3 | S | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:145:5:147:7 | s_method | calls.rb:144:1:148:3 | S | +| calls.rb:146:9:146:12 | self | calls.rb:144:1:148:3 | S | +| calls.rb:146:9:146:17 | call to to_s | calls.rb:144:1:148:3 | S | +| calls.rb:150:1:153:3 | A | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:150:11:150:11 | S | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:151:5:152:7 | to_s | calls.rb:150:1:153:3 | A | +| calls.rb:155:1:158:3 | B | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:155:11:155:11 | S | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:156:5:157:7 | to_s | calls.rb:155:1:158:3 | B | +| calls.rb:160:1:160:1 | S | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:160:1:160:5 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:160:1:160:14 | call to s_method | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:161:1:161:1 | A | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:161:1:161:5 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:161:1:161:14 | call to s_method | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:162:1:162:1 | B | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:162:1:162:5 | call to new | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:162:1:162:14 | call to s_method | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:164:1:165:3 | private_on_main | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:167:1:167:15 | call to private_on_main | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:167:1:167:15 | self | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:169:1:184:3 | Singletons | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:170:5:172:7 | singleton_a | calls.rb:169:1:184:3 | Singletons | +| calls.rb:170:9:170:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:171:9:171:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:171:9:171:24 | call to singleton_b | calls.rb:169:1:184:3 | Singletons | +| calls.rb:174:5:176:7 | singleton_b | calls.rb:169:1:184:3 | Singletons | +| calls.rb:174:9:174:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:175:9:175:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:175:9:175:24 | call to singleton_c | calls.rb:169:1:184:3 | Singletons | +| calls.rb:178:5:179:7 | singleton_c | calls.rb:169:1:184:3 | Singletons | +| calls.rb:178:9:178:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:181:5:183:7 | singleton_d | calls.rb:169:1:184:3 | Singletons | +| calls.rb:181:9:181:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:182:9:182:12 | self | calls.rb:169:1:184:3 | Singletons | +| calls.rb:182:9:182:24 | call to singleton_a | calls.rb:169:1:184:3 | Singletons | +| calls.rb:186:1:186:10 | Singletons | calls.rb:1:1:186:22 | calls.rb | +| calls.rb:186:1:186:22 | call to singleton_a | calls.rb:1:1:186:22 | calls.rb | +| hello.rb:1:1:8:3 | EnglishWords | hello.rb:1:1:22:3 | hello.rb | +| hello.rb:2:5:4:7 | hello | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:3:9:3:22 | return | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:3:16:3:22 | "hello" | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:3:17:3:21 | hello | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:5:5:7:7 | world | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:6:9:6:22 | return | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:6:16:6:22 | "world" | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:6:17:6:21 | world | hello.rb:1:1:8:3 | EnglishWords | +| hello.rb:11:1:16:3 | Greeting | hello.rb:1:1:22:3 | hello.rb | +| hello.rb:12:5:12:24 | call to include | hello.rb:11:1:16:3 | Greeting | +| hello.rb:12:5:12:24 | self | hello.rb:11:1:16:3 | Greeting | +| hello.rb:12:13:12:24 | EnglishWords | hello.rb:11:1:16:3 | Greeting | +| hello.rb:13:5:15:7 | message | hello.rb:11:1:16:3 | Greeting | +| hello.rb:14:9:14:20 | return | hello.rb:11:1:16:3 | Greeting | +| hello.rb:14:16:14:20 | call to hello | hello.rb:11:1:16:3 | Greeting | +| hello.rb:14:16:14:20 | self | hello.rb:11:1:16:3 | Greeting | +| hello.rb:18:1:22:3 | HelloWorld | hello.rb:1:1:22:3 | hello.rb | +| hello.rb:18:20:18:27 | Greeting | hello.rb:1:1:22:3 | hello.rb | +| hello.rb:19:5:21:7 | message | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:9:20:40 | return | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:16:20:20 | call to super | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:16:20:26 | ... + ... | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:16:20:34 | ... + ... | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:16:20:40 | ... + ... | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:24:20:26 | " " | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:25:20:25 | | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:30:20:34 | call to world | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:30:20:34 | self | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:38:20:40 | "!" | hello.rb:18:1:22:3 | HelloWorld | +| hello.rb:20:39:20:39 | ! | hello.rb:18:1:22:3 | HelloWorld | +| modules.rb:1:1:2:3 | Empty | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:4:1:24:3 | Foo | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:5:3:14:5 | Bar | modules.rb:4:1:24:3 | Foo | +| modules.rb:6:5:7:7 | ClassInFooBar | modules.rb:5:3:14:5 | Bar | +| modules.rb:9:5:10:7 | method_in_foo_bar | modules.rb:5:3:14:5 | Bar | +| modules.rb:12:5:12:26 | call to puts | modules.rb:5:3:14:5 | Bar | +| modules.rb:12:5:12:26 | self | modules.rb:5:3:14:5 | Bar | +| modules.rb:12:10:12:26 | "module Foo::Bar" | modules.rb:5:3:14:5 | Bar | +| modules.rb:12:11:12:25 | module Foo::Bar | modules.rb:5:3:14:5 | Bar | +| modules.rb:13:5:13:15 | $global_var | modules.rb:5:3:14:5 | Bar | +| modules.rb:13:5:13:19 | ... = ... | modules.rb:5:3:14:5 | Bar | +| modules.rb:13:19:13:19 | 0 | modules.rb:5:3:14:5 | Bar | +| modules.rb:16:3:17:5 | method_in_foo | modules.rb:4:1:24:3 | Foo | +| modules.rb:19:3:20:5 | ClassInFoo | modules.rb:4:1:24:3 | Foo | +| modules.rb:22:3:22:19 | call to puts | modules.rb:4:1:24:3 | Foo | +| modules.rb:22:3:22:19 | self | modules.rb:4:1:24:3 | Foo | +| modules.rb:22:8:22:19 | "module Foo" | modules.rb:4:1:24:3 | Foo | +| modules.rb:22:9:22:18 | module Foo | modules.rb:4:1:24:3 | Foo | +| modules.rb:23:3:23:13 | $global_var | modules.rb:4:1:24:3 | Foo | +| modules.rb:23:3:23:17 | ... = ... | modules.rb:4:1:24:3 | Foo | +| modules.rb:23:17:23:17 | 1 | modules.rb:4:1:24:3 | Foo | +| modules.rb:26:1:35:3 | Foo | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:27:3:28:5 | method_in_another_definition_of_foo | modules.rb:26:1:35:3 | Foo | +| modules.rb:30:3:31:5 | ClassInAnotherDefinitionOfFoo | modules.rb:26:1:35:3 | Foo | +| modules.rb:33:3:33:25 | call to puts | modules.rb:26:1:35:3 | Foo | +| modules.rb:33:3:33:25 | self | modules.rb:26:1:35:3 | Foo | +| modules.rb:33:8:33:25 | "module Foo again" | modules.rb:26:1:35:3 | Foo | +| modules.rb:33:9:33:24 | module Foo again | modules.rb:26:1:35:3 | Foo | +| modules.rb:34:3:34:13 | $global_var | modules.rb:26:1:35:3 | Foo | +| modules.rb:34:3:34:17 | ... = ... | modules.rb:26:1:35:3 | Foo | +| modules.rb:34:17:34:17 | 2 | modules.rb:26:1:35:3 | Foo | +| modules.rb:37:1:46:3 | Bar | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:38:3:39:5 | method_a | modules.rb:37:1:46:3 | Bar | +| modules.rb:41:3:42:5 | method_b | modules.rb:37:1:46:3 | Bar | +| modules.rb:44:3:44:19 | call to puts | modules.rb:37:1:46:3 | Bar | +| modules.rb:44:3:44:19 | self | modules.rb:37:1:46:3 | Bar | +| modules.rb:44:8:44:19 | "module Bar" | modules.rb:37:1:46:3 | Bar | +| modules.rb:44:9:44:18 | module Bar | modules.rb:37:1:46:3 | Bar | +| modules.rb:45:3:45:13 | $global_var | modules.rb:37:1:46:3 | Bar | +| modules.rb:45:3:45:17 | ... = ... | modules.rb:37:1:46:3 | Bar | +| modules.rb:45:17:45:17 | 3 | modules.rb:37:1:46:3 | Bar | +| modules.rb:48:1:57:3 | Bar | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:48:8:48:10 | Foo | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:49:3:50:5 | ClassInAnotherDefinitionOfFooBar | modules.rb:48:1:57:3 | Bar | +| modules.rb:52:3:53:5 | method_in_another_definition_of_foo_bar | modules.rb:48:1:57:3 | Bar | +| modules.rb:55:3:55:30 | call to puts | modules.rb:48:1:57:3 | Bar | +| modules.rb:55:3:55:30 | self | modules.rb:48:1:57:3 | Bar | +| modules.rb:55:8:55:30 | "module Foo::Bar again" | modules.rb:48:1:57:3 | Bar | +| modules.rb:55:9:55:29 | module Foo::Bar again | modules.rb:48:1:57:3 | Bar | +| modules.rb:56:3:56:13 | $global_var | modules.rb:48:1:57:3 | Bar | +| modules.rb:56:3:56:17 | ... = ... | modules.rb:48:1:57:3 | Bar | +| modules.rb:56:17:56:17 | 4 | modules.rb:48:1:57:3 | Bar | +| modules.rb:60:1:61:3 | MyModuleInGlobalScope | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:63:1:81:3 | Test | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:65:3:68:5 | Foo1 | modules.rb:63:1:81:3 | Test | +| modules.rb:66:5:67:7 | Bar | modules.rb:65:3:68:5 | Foo1 | +| modules.rb:66:11:66:14 | Foo1 | modules.rb:65:3:68:5 | Foo1 | +| modules.rb:70:3:74:5 | Foo2 | modules.rb:63:1:81:3 | Test | +| modules.rb:71:5:71:19 | Foo2 | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:72:5:73:7 | Bar | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:72:11:72:14 | Foo2 | modules.rb:70:3:74:5 | Foo2 | +| modules.rb:76:3:80:5 | Foo3 | modules.rb:63:1:81:3 | Test | +| modules.rb:77:5:77:8 | Foo3 | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:77:5:77:17 | ... = ... | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:77:12:77:17 | Object | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:78:5:79:7 | Bar | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:78:11:78:14 | Foo3 | modules.rb:76:3:80:5 | Foo3 | +| modules.rb:83:1:86:3 | Other | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:84:3:85:5 | Foo1 | modules.rb:83:1:86:3 | Other | +| modules.rb:88:1:93:3 | IncludeTest | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:89:3:89:16 | call to include | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:89:3:89:16 | self | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:89:11:89:16 | Test | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:3:90:8 | Object | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:3:90:38 | call to module_eval | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:22:90:38 | { ... } | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:24:90:36 | call to prepend | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:24:90:36 | self | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:90:32:90:36 | Other | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:3:92:5 | Y | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:91:10:91:13 | Foo1 | modules.rb:88:1:93:3 | IncludeTest | +| modules.rb:95:1:99:3 | IncludeTest2 | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:96:3:96:14 | call to include | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:96:3:96:14 | self | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:96:11:96:14 | Test | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:3:98:5 | Z | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:97:10:97:13 | Foo1 | modules.rb:95:1:99:3 | IncludeTest2 | +| modules.rb:101:1:105:3 | PrependTest | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:102:3:102:16 | call to prepend | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:102:3:102:16 | self | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:102:11:102:16 | Test | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:3:104:5 | Y | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:103:10:103:13 | Foo2 | modules.rb:101:1:105:3 | PrependTest | +| modules.rb:107:1:110:3 | MM | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:108:3:109:5 | MM | modules.rb:107:1:110:3 | MM | +| modules.rb:108:10:108:11 | MM | modules.rb:107:1:110:3 | MM | +| modules.rb:112:1:113:3 | YY | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:115:1:121:3 | XX | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:116:7:117:9 | YY | modules.rb:115:1:121:3 | XX | +| modules.rb:116:18:116:19 | YY | modules.rb:115:1:121:3 | XX | +| modules.rb:119:7:120:9 | class << ... | modules.rb:115:1:121:3 | XX | +| modules.rb:119:16:119:19 | self | modules.rb:115:1:121:3 | XX | +| modules.rb:123:1:124:3 | Baz | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:123:8:123:11 | Test | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:123:8:123:17 | Foo1 | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:123:8:123:22 | Bar | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:126:1:126:2 | xx | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:126:1:126:11 | ... = ... | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:126:6:126:7 | XX | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:126:6:126:11 | call to new | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:128:1:129:3 | class << ... | modules.rb:1:1:129:4 | modules.rb | +| modules.rb:128:10:128:11 | xx | modules.rb:1:1:129:4 | modules.rb | +| modules_rec.rb:1:1:2:3 | A | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:1:7:1:7 | B | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:4:1:5:3 | B | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:4:7:4:7 | A | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:7:1:9:3 | A | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:7:11:7:11 | B | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:8:3:8:11 | call to prepend | modules_rec.rb:7:1:9:3 | A | +| modules_rec.rb:8:3:8:11 | self | modules_rec.rb:7:1:9:3 | A | +| modules_rec.rb:8:11:8:11 | B | modules_rec.rb:7:1:9:3 | A | +| modules_rec.rb:11:1:11:9 | call to prepend | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:11:1:11:9 | self | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| modules_rec.rb:11:9:11:9 | A | modules_rec.rb:1:1:11:26 | modules_rec.rb | +| private.rb:1:1:29:3 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:2:3:3:5 | call to private | private.rb:1:1:29:3 | E | +| private.rb:2:3:3:5 | self | private.rb:1:1:29:3 | E | +| private.rb:2:11:3:5 | private1 | private.rb:1:1:29:3 | E | +| private.rb:5:3:6:5 | public | private.rb:1:1:29:3 | E | +| private.rb:8:3:9:5 | private2 | private.rb:1:1:29:3 | E | +| private.rb:10:3:10:19 | call to private | private.rb:1:1:29:3 | E | +| private.rb:10:3:10:19 | self | private.rb:1:1:29:3 | E | +| private.rb:10:11:10:19 | :private2 | private.rb:1:1:29:3 | E | +| private.rb:10:11:10:19 | private2 | private.rb:1:1:29:3 | E | +| private.rb:12:3:12:9 | call to private | private.rb:1:1:29:3 | E | +| private.rb:12:3:12:9 | self | private.rb:1:1:29:3 | E | +| private.rb:14:3:15:5 | private3 | private.rb:1:1:29:3 | E | +| private.rb:17:3:18:5 | private4 | private.rb:1:1:29:3 | E | +| private.rb:20:3:21:5 | public2 | private.rb:1:1:29:3 | E | +| private.rb:20:7:20:10 | self | private.rb:1:1:29:3 | E | +| private.rb:23:3:24:5 | call to private_class_method | private.rb:1:1:29:3 | E | +| private.rb:23:3:24:5 | self | private.rb:1:1:29:3 | E | +| private.rb:23:24:24:5 | private5 | private.rb:1:1:29:3 | E | +| private.rb:23:28:23:31 | self | private.rb:1:1:29:3 | E | +| private.rb:26:3:27:5 | private6 | private.rb:1:1:29:3 | E | +| private.rb:26:7:26:10 | self | private.rb:1:1:29:3 | E | +| private.rb:28:3:28:32 | call to private_class_method | private.rb:1:1:29:3 | E | +| private.rb:28:3:28:32 | self | private.rb:1:1:29:3 | E | +| private.rb:28:24:28:32 | :private6 | private.rb:1:1:29:3 | E | +| private.rb:28:24:28:32 | private6 | private.rb:1:1:29:3 | E | +| private.rb:31:1:32:3 | private_on_main | private.rb:1:1:60:3 | private.rb | +| private.rb:34:1:34:1 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:34:1:34:5 | call to new | private.rb:1:1:60:3 | private.rb | +| private.rb:34:1:34:14 | call to private1 | private.rb:1:1:60:3 | private.rb | +| private.rb:35:1:35:1 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:35:1:35:5 | call to new | private.rb:1:1:60:3 | private.rb | +| private.rb:35:1:35:14 | call to private2 | private.rb:1:1:60:3 | private.rb | +| private.rb:36:1:36:1 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:36:1:36:5 | call to new | private.rb:1:1:60:3 | private.rb | +| private.rb:36:1:36:14 | call to private3 | private.rb:1:1:60:3 | private.rb | +| private.rb:37:1:37:1 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:37:1:37:5 | call to new | private.rb:1:1:60:3 | private.rb | +| private.rb:37:1:37:14 | call to private4 | private.rb:1:1:60:3 | private.rb | +| private.rb:38:1:38:1 | E | private.rb:1:1:60:3 | private.rb | +| private.rb:38:1:38:5 | call to new | private.rb:1:1:60:3 | private.rb | +| private.rb:38:1:38:12 | call to public | private.rb:1:1:60:3 | private.rb | +| private.rb:40:1:40:15 | call to private_on_main | private.rb:1:1:60:3 | private.rb | +| private.rb:40:1:40:15 | self | private.rb:1:1:60:3 | private.rb | +| private.rb:42:1:60:3 | F | private.rb:1:1:60:3 | private.rb | +| private.rb:43:3:44:5 | call to private | private.rb:42:1:60:3 | F | +| private.rb:43:3:44:5 | self | private.rb:42:1:60:3 | F | +| private.rb:43:11:44:5 | private1 | private.rb:42:1:60:3 | F | +| private.rb:46:3:47:5 | public | private.rb:42:1:60:3 | F | +| private.rb:49:3:50:5 | private2 | private.rb:42:1:60:3 | F | +| private.rb:51:3:51:19 | call to private | private.rb:42:1:60:3 | F | +| private.rb:51:3:51:19 | self | private.rb:42:1:60:3 | F | +| private.rb:51:11:51:19 | :private2 | private.rb:42:1:60:3 | F | +| private.rb:51:11:51:19 | private2 | private.rb:42:1:60:3 | F | +| private.rb:53:3:53:9 | call to private | private.rb:42:1:60:3 | F | +| private.rb:53:3:53:9 | self | private.rb:42:1:60:3 | F | +| private.rb:55:3:56:5 | private3 | private.rb:42:1:60:3 | F | +| private.rb:58:3:59:5 | private4 | private.rb:42:1:60:3 | F | diff --git a/ruby/ql/test/library-tests/modules/modules.ql b/ruby/ql/test/library-tests/modules/modules.ql index d5c8207dc2c..792ace8ecd8 100644 --- a/ruby/ql/test/library-tests/modules/modules.ql +++ b/ruby/ql/test/library-tests/modules/modules.ql @@ -18,3 +18,5 @@ query predicate resolveConstantReadAccess(ConstantReadAccess a, string s) { query predicate resolveConstantWriteAccess(ConstantWriteAccess c, string s) { s = Internal::resolveConstantWriteAccess(c) } + +query predicate enclosingModule(AstNode n, ModuleBase m) { m = n.getEnclosingModule() } diff --git a/ruby/ql/test/library-tests/modules/modules.rb b/ruby/ql/test/library-tests/modules/modules.rb index 764ba455ba0..4c30e3b4596 100644 --- a/ruby/ql/test/library-tests/modules/modules.rb +++ b/ruby/ql/test/library-tests/modules/modules.rb @@ -115,7 +115,15 @@ end module XX class YY < YY end + + class << self + end end module Test::Foo1::Bar::Baz end + +xx = XX.new + +class << xx +end diff --git a/ruby/ql/test/library-tests/modules/superclasses.expected b/ruby/ql/test/library-tests/modules/superclasses.expected index d0185dddd6f..9a7415ec797 100644 --- a/ruby/ql/test/library-tests/modules/superclasses.expected +++ b/ruby/ql/test/library-tests/modules/superclasses.expected @@ -147,7 +147,7 @@ modules.rb: # 116| XX::YY #-----| -> YY -# 120| Test::Foo1::Bar::Baz +# 123| Test::Foo1::Bar::Baz modules_rec.rb: # 1| B::A diff --git a/shared/typos/codeql/typos/TypoDatabase.qll b/shared/typos/codeql/typos/TypoDatabase.qll new file mode 100644 index 00000000000..a41f003a8c0 --- /dev/null +++ b/shared/typos/codeql/typos/TypoDatabase.qll @@ -0,0 +1,9035 @@ +/** + * Holds if `wrong` is a common misspelling of `right`. + * + * This predicate was automatically generated by + * python buildutils-internal/scripts/generate-typos.py + * which uses http://en.wikipedia.org/wiki/Wikipedia:Lists_of_common_misspellings/For_machines (with custom additions) on 2018-01-19. + * + * This file is available under the Creative Commons Attribution-ShareAlike License + * (https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License). + */ +predicate typos(string wrong, string right) { + wrong = "abandonned" and right = "abandoned" + or + wrong = "abbout" and right = "about" + or + wrong = "aberation" and right = "aberration" + or + wrong = "abilityes" and right = "abilities" + or + wrong = "abilties" and right = "abilities" + or + wrong = "abilty" and right = "ability" + or + wrong = "abondon" and right = "abandon" + or + wrong = "abondoned" and right = "abandoned" + or + wrong = "abondoning" and right = "abandoning" + or + wrong = "abondons" and right = "abandons" + or + wrong = "aborigene" and right = "aborigine" + or + wrong = "abortificant" and right = "abortifacient" + or + wrong = "abotu" and right = "about" + or + wrong = "abreviate" and right = "abbreviate" + or + wrong = "abreviated" and right = "abbreviated" + or + wrong = "abreviation" and right = "abbreviation" + or + wrong = "abritrary" and right = "arbitrary" + or + wrong = "absail" and right = "abseil" + or + wrong = "absailing" and right = "abseiling" + or + wrong = "abscence" and right = "absence" + or + wrong = "absense" and right = "absence" + or + wrong = "absolutly" and right = "absolutely" + or + wrong = "absorbsion" and right = "absorption" + or + wrong = "absorbtion" and right = "absorption" + or + wrong = "abudance" and right = "abundance" + or + wrong = "abundacies" and right = "abundances" + or + wrong = "abundancies" and right = "abundances" + or + wrong = "abundunt" and right = "abundant" + or + wrong = "abutts" and right = "abuts" + or + wrong = "acadamy" and right = "academy" + or + wrong = "acadmic" and right = "academic" + or + wrong = "accademic" and right = "academic" + or + wrong = "accademy" and right = "academy" + or + wrong = "acccess" and right = "access" + or + wrong = "acccused" and right = "accused" + or + wrong = "accelleration" and right = "acceleration" + or + wrong = "accension" and right = "accession" + or + wrong = "accension" and right = "ascension" + or + wrong = "acceptence" and right = "acceptance" + or + wrong = "acceptible" and right = "acceptable" + or + wrong = "accesories" and right = "accessories" + or + wrong = "accessable" and right = "accessible" + or + wrong = "accidant" and right = "accident" + or + wrong = "accidentaly" and right = "accidentally" + or + wrong = "accidently" and right = "accidentally" + or + wrong = "acclimitization" and right = "acclimatization" + or + wrong = "accomadate" and right = "accommodate" + or + wrong = "accomadated" and right = "accommodated" + or + wrong = "accomadates" and right = "accommodates" + or + wrong = "accomadating" and right = "accommodating" + or + wrong = "accomadation" and right = "accommodation" + or + wrong = "accomadations" and right = "accommodations" + or + wrong = "accomdate" and right = "accommodate" + or + wrong = "accomodate" and right = "accommodate" + or + wrong = "accomodated" and right = "accommodated" + or + wrong = "accomodates" and right = "accommodates" + or + wrong = "accomodating" and right = "accommodating" + or + wrong = "accomodation" and right = "accommodation" + or + wrong = "accomodations" and right = "accommodations" + or + wrong = "accompanyed" and right = "accompanied" + or + wrong = "accordeon" and right = "accordion" + or + wrong = "accordian" and right = "accordion" + or + wrong = "accoring" and right = "according" + or + wrong = "accoustic" and right = "acoustic" + or + wrong = "accquainted" and right = "acquainted" + or + wrong = "accrediation" and right = "accreditation" + or + wrong = "accredidation" and right = "accreditation" + or + wrong = "accross" and right = "across" + or + wrong = "accussed" and right = "accused" + or + wrong = "acedemic" and right = "academic" + or + wrong = "acheive" and right = "achieve" + or + wrong = "acheived" and right = "achieved" + or + wrong = "acheivement" and right = "achievement" + or + wrong = "acheivements" and right = "achievements" + or + wrong = "acheives" and right = "achieves" + or + wrong = "acheiving" and right = "achieving" + or + wrong = "acheivment" and right = "achievement" + or + wrong = "acheivments" and right = "achievements" + or + wrong = "achievment" and right = "achievement" + or + wrong = "achievments" and right = "achievements" + or + wrong = "achive" and right = "achieve" + or + wrong = "achive" and right = "archive" + or + wrong = "achived" and right = "achieved" + or + wrong = "achived" and right = "archived" + or + wrong = "achivement" and right = "achievement" + or + wrong = "achivements" and right = "achievements" + or + wrong = "acident" and right = "accident" + or + wrong = "acknowldeged" and right = "acknowledged" + or + wrong = "acknowledgeing" and right = "acknowledging" + or + wrong = "ackward" and right = "awkward" + or + wrong = "ackward" and right = "backward" + or + wrong = "acommodate" and right = "accommodate" + or + wrong = "acomplish" and right = "accomplish" + or + wrong = "acomplished" and right = "accomplished" + or + wrong = "acomplishment" and right = "accomplishment" + or + wrong = "acomplishments" and right = "accomplishments" + or + wrong = "acording" and right = "according" + or + wrong = "acordingly" and right = "accordingly" + or + wrong = "acquaintence" and right = "acquaintance" + or + wrong = "acquaintences" and right = "acquaintances" + or + wrong = "acquiantence" and right = "acquaintance" + or + wrong = "acquiantences" and right = "acquaintances" + or + wrong = "acquited" and right = "acquitted" + or + wrong = "activites" and right = "activities" + or + wrong = "activly" and right = "actively" + or + wrong = "actualy" and right = "actually" + or + wrong = "acuracy" and right = "accuracy" + or + wrong = "acused" and right = "accused" + or + wrong = "acustom" and right = "accustom" + or + wrong = "acustommed" and right = "accustomed" + or + wrong = "adapater" and right = "adapter" + or + wrong = "adavanced" and right = "advanced" + or + wrong = "adbandon" and right = "abandon" + or + wrong = "addional" and right = "additional" + or + wrong = "addionally" and right = "additionally" + or + wrong = "additinally" and right = "additionally" + or + wrong = "additionaly" and right = "additionally" + or + wrong = "additonal" and right = "additional" + or + wrong = "additonally" and right = "additionally" + or + wrong = "addmission" and right = "admission" + or + wrong = "addopt" and right = "adopt" + or + wrong = "addopted" and right = "adopted" + or + wrong = "addoptive" and right = "adoptive" + or + wrong = "addres" and right = "adders" + or + wrong = "addres" and right = "address" + or + wrong = "addresable" and right = "addressable" + or + wrong = "addresed" and right = "addressed" + or + wrong = "addresing" and right = "addressing" + or + wrong = "addressess" and right = "addresses" + or + wrong = "addtion" and right = "addition" + or + wrong = "addtional" and right = "additional" + or + wrong = "adecuate" and right = "adequate" + or + wrong = "adequit" and right = "adequate" + or + wrong = "adhearing" and right = "adhering" + or + wrong = "adherance" and right = "adherence" + or + wrong = "admendment" and right = "amendment" + or + wrong = "admininistrative" and right = "administrative" + or + wrong = "adminstered" and right = "administered" + or + wrong = "adminstrate" and right = "administrate" + or + wrong = "adminstration" and right = "administration" + or + wrong = "adminstrative" and right = "administrative" + or + wrong = "adminstrator" and right = "administrator" + or + wrong = "admissability" and right = "admissibility" + or + wrong = "admissable" and right = "admissible" + or + wrong = "admited" and right = "admitted" + or + wrong = "admitedly" and right = "admittedly" + or + wrong = "adn" and right = "and" + or + wrong = "adolecent" and right = "adolescent" + or + wrong = "adquire" and right = "acquire" + or + wrong = "adquired" and right = "acquired" + or + wrong = "adquires" and right = "acquires" + or + wrong = "adquiring" and right = "acquiring" + or + wrong = "adres" and right = "address" + or + wrong = "adresable" and right = "addressable" + or + wrong = "adresing" and right = "addressing" + or + wrong = "adress" and right = "address" + or + wrong = "adressable" and right = "addressable" + or + wrong = "adressed" and right = "addressed" + or + wrong = "adressing" and right = "addressing" + or + wrong = "adressing" and right = "dressing" + or + wrong = "adventrous" and right = "adventurous" + or + wrong = "advertisment" and right = "advertisement" + or + wrong = "advertisments" and right = "advertisements" + or + wrong = "advesary" and right = "adversary" + or + wrong = "adviced" and right = "advised" + or + wrong = "aeriel" and right = "aerial" + or + wrong = "aeriels" and right = "aerials" + or + wrong = "afair" and right = "affair" + or + wrong = "afficianados" and right = "aficionados" + or + wrong = "afficionado" and right = "aficionado" + or + wrong = "afficionados" and right = "aficionados" + or + wrong = "affilate" and right = "affiliate" + or + wrong = "affilliate" and right = "affiliate" + or + wrong = "affort" and right = "afford" + or + wrong = "affort" and right = "effort" + or + wrong = "aforememtioned" and right = "aforementioned" + or + wrong = "againnst" and right = "against" + or + wrong = "agains" and right = "against" + or + wrong = "agaisnt" and right = "against" + or + wrong = "aganist" and right = "against" + or + wrong = "aggaravates" and right = "aggravates" + or + wrong = "aggreed" and right = "agreed" + or + wrong = "aggreement" and right = "agreement" + or + wrong = "aggregious" and right = "egregious" + or + wrong = "aggresive" and right = "aggressive" + or + wrong = "agian" and right = "again" + or + wrong = "agianst" and right = "against" + or + wrong = "agin" and right = "again" + or + wrong = "agina" and right = "again" + or + wrong = "agina" and right = "angina" + or + wrong = "aginst" and right = "against" + or + wrong = "agravate" and right = "aggravate" + or + wrong = "agre" and right = "agree" + or + wrong = "agred" and right = "agreed" + or + wrong = "agreeement" and right = "agreement" + or + wrong = "agreemnt" and right = "agreement" + or + wrong = "agregate" and right = "aggregate" + or + wrong = "agregates" and right = "aggregates" + or + wrong = "agreing" and right = "agreeing" + or + wrong = "agression" and right = "aggression" + or + wrong = "agressive" and right = "aggressive" + or + wrong = "agressively" and right = "aggressively" + or + wrong = "agressor" and right = "aggressor" + or + wrong = "agricultue" and right = "agriculture" + or + wrong = "agriculure" and right = "agriculture" + or + wrong = "agricuture" and right = "agriculture" + or + wrong = "agrieved" and right = "aggrieved" + or + wrong = "agrument" and right = "argument" + or + wrong = "agruments" and right = "arguments" + or + wrong = "ahev" and right = "have" + or + wrong = "ahppen" and right = "happen" + or + wrong = "ahve" and right = "have" + or + wrong = "aicraft" and right = "aircraft" + or + wrong = "aiport" and right = "airport" + or + wrong = "airbourne" and right = "airborne" + or + wrong = "aircaft" and right = "aircraft" + or + wrong = "aircrafts" and right = "aircraft" + or + wrong = "airporta" and right = "airports" + or + wrong = "airrcraft" and right = "aircraft" + or + wrong = "aisian" and right = "asian" + or + wrong = "albiet" and right = "albeit" + or + wrong = "alchohol" and right = "alcohol" + or + wrong = "alchoholic" and right = "alcoholic" + or + wrong = "alchol" and right = "alcohol" + or + wrong = "alcholic" and right = "alcoholic" + or + wrong = "alcohal" and right = "alcohol" + or + wrong = "alcoholical" and right = "alcoholic" + or + wrong = "aledge" and right = "allege" + or + wrong = "aledged" and right = "alleged" + or + wrong = "aledges" and right = "alleges" + or + wrong = "alege" and right = "allege" + or + wrong = "aleged" and right = "alleged" + or + wrong = "alegience" and right = "allegiance" + or + wrong = "algebraical" and right = "algebraic" + or + wrong = "algorhitms" and right = "algorithms" + or + wrong = "algoritm" and right = "algorithm" + or + wrong = "algoritms" and right = "algorithms" + or + wrong = "alientating" and right = "alienating" + or + wrong = "alledge" and right = "allege" + or + wrong = "alledged" and right = "alleged" + or + wrong = "alledgedly" and right = "allegedly" + or + wrong = "alledges" and right = "alleges" + or + wrong = "allegedely" and right = "allegedly" + or + wrong = "allegedy" and right = "allegedly" + or + wrong = "allegely" and right = "allegedly" + or + wrong = "allegence" and right = "allegiance" + or + wrong = "allegience" and right = "allegiance" + or + wrong = "allign" and right = "align" + or + wrong = "alligned" and right = "aligned" + or + wrong = "alliviate" and right = "alleviate" + or + wrong = "allopone" and right = "allophone" + or + wrong = "allopones" and right = "allophones" + or + wrong = "allready" and right = "already" + or + wrong = "allthough" and right = "although" + or + wrong = "alltogether" and right = "altogether" + or + wrong = "almsot" and right = "almost" + or + wrong = "alochol" and right = "alcohol" + or + wrong = "alomst" and right = "almost" + or + wrong = "alot" and right = "allot" + or + wrong = "alotted" and right = "allotted" + or + wrong = "alowed" and right = "allowed" + or + wrong = "alowing" and right = "allowing" + or + wrong = "alreayd" and right = "already" + or + wrong = "alse" and right = "else" + or + wrong = "alsot" and right = "also" + or + wrong = "alternitives" and right = "alternatives" + or + wrong = "altho" and right = "although" + or + wrong = "althought" and right = "although" + or + wrong = "altough" and right = "although" + or + wrong = "alusion" and right = "allusion" + or + wrong = "alusion" and right = "illusion" + or + wrong = "alwasy" and right = "always" + or + wrong = "alwyas" and right = "always" + or + wrong = "amalgomated" and right = "amalgamated" + or + wrong = "amatuer" and right = "amateur" + or + wrong = "amature" and right = "amateur" + or + wrong = "amature" and right = "armature" + or + wrong = "amendmant" and right = "amendment" + or + wrong = "amercia" and right = "america" + or + wrong = "amerliorate" and right = "ameliorate" + or + wrong = "amke" and right = "make" + or + wrong = "amking" and right = "making" + or + wrong = "ammend" and right = "amend" + or + wrong = "ammended" and right = "amended" + or + wrong = "ammendment" and right = "amendment" + or + wrong = "ammendments" and right = "amendments" + or + wrong = "ammount" and right = "amount" + or + wrong = "ammused" and right = "amused" + or + wrong = "amoung" and right = "among" + or + wrong = "amoungst" and right = "amongst" + or + wrong = "amung" and right = "among" + or + wrong = "amunition" and right = "ammunition" + or + wrong = "analagous" and right = "analogous" + or + wrong = "analitic" and right = "analytic" + or + wrong = "analogeous" and right = "analogous" + or + wrong = "anarchim" and right = "anarchism" + or + wrong = "anarchistm" and right = "anarchism" + or + wrong = "anbd" and right = "and" + or + wrong = "ancestory" and right = "ancestry" + or + wrong = "ancilliary" and right = "ancillary" + or + wrong = "andd" and right = "and" + or + wrong = "androgenous" and right = "androgynous" + or + wrong = "androgeny" and right = "androgyny" + or + wrong = "anihilation" and right = "annihilation" + or + wrong = "aniversary" and right = "anniversary" + or + wrong = "annoint" and right = "anoint" + or + wrong = "annointed" and right = "anointed" + or + wrong = "annointing" and right = "anointing" + or + wrong = "annoints" and right = "anoints" + or + wrong = "annouced" and right = "announced" + or + wrong = "annualy" and right = "annually" + or + wrong = "annuled" and right = "annulled" + or + wrong = "anohter" and right = "another" + or + wrong = "anomolies" and right = "anomalies" + or + wrong = "anomolous" and right = "anomalous" + or + wrong = "anomoly" and right = "anomaly" + or + wrong = "anonimity" and right = "anonymity" + or + wrong = "anounced" and right = "announced" + or + wrong = "anouncement" and right = "announcement" + or + wrong = "ansalisation" and right = "nasalisation" + or + wrong = "ansalization" and right = "nasalization" + or + wrong = "ansestors" and right = "ancestors" + or + wrong = "antartic" and right = "antarctic" + or + wrong = "anthromorphization" and right = "anthropomorphization" + or + wrong = "anthropolgist" and right = "anthropologist" + or + wrong = "anthropolgy" and right = "anthropology" + or + wrong = "anual" and right = "annual" + or + wrong = "anulled" and right = "annulled" + or + wrong = "anwsered" and right = "answered" + or + wrong = "anyhwere" and right = "anywhere" + or + wrong = "anytying" and right = "anything" + or + wrong = "aparent" and right = "apparent" + or + wrong = "aparment" and right = "apartment" + or + wrong = "apenines" and right = "apennines" + or + wrong = "aplication" and right = "application" + or + wrong = "aplied" and right = "applied" + or + wrong = "apolegetics" and right = "apologetics" + or + wrong = "apon" and right = "apron" + or + wrong = "apon" and right = "upon" + or + wrong = "apparant" and right = "apparent" + or + wrong = "apparantly" and right = "apparently" + or + wrong = "appart" and right = "apart" + or + wrong = "appartment" and right = "apartment" + or + wrong = "appartments" and right = "apartments" + or + wrong = "appealling" and right = "appalling" + or + wrong = "appealling" and right = "appealing" + or + wrong = "appeareance" and right = "appearance" + or + wrong = "appearence" and right = "appearance" + or + wrong = "appearences" and right = "appearances" + or + wrong = "appenines" and right = "apennines" + or + wrong = "apperance" and right = "appearance" + or + wrong = "apperances" and right = "appearances" + or + wrong = "appereance" and right = "appearance" + or + wrong = "appereances" and right = "appearances" + or + wrong = "applicaiton" and right = "application" + or + wrong = "applicaitons" and right = "applications" + or + wrong = "appologies" and right = "apologies" + or + wrong = "appology" and right = "apology" + or + wrong = "apprearance" and right = "appearance" + or + wrong = "apprieciate" and right = "appreciate" + or + wrong = "approachs" and right = "approaches" + or + wrong = "appropiate" and right = "appropriate" + or + wrong = "appropraite" and right = "appropriate" + or + wrong = "appropropiate" and right = "appropriate" + or + wrong = "approproximate" and right = "approximate" + or + wrong = "approxamately" and right = "approximately" + or + wrong = "approxiately" and right = "approximately" + or + wrong = "approximitely" and right = "approximately" + or + wrong = "aprehensive" and right = "apprehensive" + or + wrong = "apropriate" and right = "appropriate" + or + wrong = "aproval" and right = "approval" + or + wrong = "aproximate" and right = "approximate" + or + wrong = "aproximately" and right = "approximately" + or + wrong = "aquaduct" and right = "aqueduct" + or + wrong = "aquaintance" and right = "acquaintance" + or + wrong = "aquainted" and right = "acquainted" + or + wrong = "aquiantance" and right = "acquaintance" + or + wrong = "aquire" and right = "acquire" + or + wrong = "aquired" and right = "acquired" + or + wrong = "aquiring" and right = "acquiring" + or + wrong = "aquisition" and right = "acquisition" + or + wrong = "aquitted" and right = "acquitted" + or + wrong = "aranged" and right = "arranged" + or + wrong = "arangement" and right = "arrangement" + or + wrong = "arbitarily" and right = "arbitrarily" + or + wrong = "arbitary" and right = "arbitrary" + or + wrong = "archaelogical" and right = "archaeological" + or + wrong = "archaelogists" and right = "archaeologists" + or + wrong = "archaelogy" and right = "archaeology" + or + wrong = "archaoelogy" and right = "archaeology" + or + wrong = "archaoelogy" and right = "archeology" + or + wrong = "archaology" and right = "archaeology" + or + wrong = "archaology" and right = "archeology" + or + wrong = "archeaologist" and right = "archaeologist" + or + wrong = "archeaologist" and right = "archeologist" + or + wrong = "archeaologists" and right = "archaeologists" + or + wrong = "archeaologists" and right = "archeologists" + or + wrong = "archetect" and right = "architect" + or + wrong = "archetects" and right = "architects" + or + wrong = "archetectural" and right = "architectural" + or + wrong = "archetecturally" and right = "architecturally" + or + wrong = "archetecture" and right = "architecture" + or + wrong = "archiac" and right = "archaic" + or + wrong = "archictect" and right = "architect" + or + wrong = "archimedian" and right = "archimedean" + or + wrong = "architecht" and right = "architect" + or + wrong = "architechturally" and right = "architecturally" + or + wrong = "architechture" and right = "architecture" + or + wrong = "architechtures" and right = "architectures" + or + wrong = "architectual" and right = "architectural" + or + wrong = "archtype" and right = "archetype" + or + wrong = "archtypes" and right = "archetypes" + or + wrong = "aready" and right = "already" + or + wrong = "areodynamics" and right = "aerodynamics" + or + wrong = "argubly" and right = "arguably" + or + wrong = "arguement" and right = "argument" + or + wrong = "arguements" and right = "arguments" + or + wrong = "arised" and right = "arose" + or + wrong = "arival" and right = "arrival" + or + wrong = "armamant" and right = "armament" + or + wrong = "armistace" and right = "armistice" + or + wrong = "arogant" and right = "arrogant" + or + wrong = "arogent" and right = "arrogant" + or + wrong = "aroud" and right = "around" + or + wrong = "arrangment" and right = "arrangement" + or + wrong = "arrangments" and right = "arrangements" + or + wrong = "arrengement" and right = "arrangement" + or + wrong = "arrengements" and right = "arrangements" + or + wrong = "arround" and right = "around" + or + wrong = "artcile" and right = "article" + or + wrong = "artical" and right = "article" + or + wrong = "artice" and right = "article" + or + wrong = "articel" and right = "article" + or + wrong = "artifical" and right = "artificial" + or + wrong = "artifically" and right = "artificially" + or + wrong = "artillary" and right = "artillery" + or + wrong = "arund" and right = "around" + or + wrong = "asetic" and right = "ascetic" + or + wrong = "asign" and right = "assign" + or + wrong = "aslo" and right = "also" + or + wrong = "asnyc" and right = "async" + or + wrong = "asociated" and right = "associated" + or + wrong = "asorbed" and right = "absorbed" + or + wrong = "asphyxation" and right = "asphyxiation" + or + wrong = "assasin" and right = "assassin" + or + wrong = "assasinate" and right = "assassinate" + or + wrong = "assasinated" and right = "assassinated" + or + wrong = "assasinates" and right = "assassinates" + or + wrong = "assasination" and right = "assassination" + or + wrong = "assasinations" and right = "assassinations" + or + wrong = "assasined" and right = "assassinated" + or + wrong = "assasins" and right = "assassins" + or + wrong = "assassintation" and right = "assassination" + or + wrong = "assemple" and right = "assemble" + or + wrong = "assertation" and right = "assertion" + or + wrong = "asside" and right = "aside" + or + wrong = "assisnate" and right = "assassinate" + or + wrong = "assit" and right = "assist" + or + wrong = "assitant" and right = "assistant" + or + wrong = "assocation" and right = "association" + or + wrong = "assoicate" and right = "associate" + or + wrong = "assoicated" and right = "associated" + or + wrong = "assoicates" and right = "associates" + or + wrong = "assosication" and right = "assassination" + or + wrong = "asssassans" and right = "assassins" + or + wrong = "assualt" and right = "assault" + or + wrong = "assualted" and right = "assaulted" + or + wrong = "assymetric" and right = "asymmetric" + or + wrong = "assymetrical" and right = "asymmetrical" + or + wrong = "asteriod" and right = "asteroid" + or + wrong = "asthetic" and right = "aesthetic" + or + wrong = "asthetical" and right = "aesthetical" + or + wrong = "asthetically" and right = "aesthetically" + or + wrong = "asume" and right = "assume" + or + wrong = "atain" and right = "attain" + or + wrong = "atempting" and right = "attempting" + or + wrong = "atheistical" and right = "atheistic" + or + wrong = "athenean" and right = "athenian" + or + wrong = "atheneans" and right = "athenians" + or + wrong = "athiesm" and right = "atheism" + or + wrong = "athiest" and right = "atheist" + or + wrong = "atorney" and right = "attorney" + or + wrong = "atribute" and right = "attribute" + or + wrong = "atributed" and right = "attributed" + or + wrong = "atributes" and right = "attributes" + or + wrong = "attaindre" and right = "attainder" + or + wrong = "attaindre" and right = "attained" + or + wrong = "attemp" and right = "attempt" + or + wrong = "attemped" and right = "attempted" + or + wrong = "attemt" and right = "attempt" + or + wrong = "attemted" and right = "attempted" + or + wrong = "attemting" and right = "attempting" + or + wrong = "attemts" and right = "attempts" + or + wrong = "attendence" and right = "attendance" + or + wrong = "attendent" and right = "attendant" + or + wrong = "attendents" and right = "attendants" + or + wrong = "attened" and right = "attended" + or + wrong = "attension" and right = "attention" + or + wrong = "attitide" and right = "attitude" + or + wrong = "attributred" and right = "attributed" + or + wrong = "attrocities" and right = "atrocities" + or + wrong = "audeince" and right = "audience" + or + wrong = "auromated" and right = "automated" + or + wrong = "austrailia" and right = "australia" + or + wrong = "austrailian" and right = "australian" + or + wrong = "auther" and right = "author" + or + wrong = "authobiographic" and right = "autobiographic" + or + wrong = "authobiography" and right = "autobiography" + or + wrong = "authorative" and right = "authoritative" + or + wrong = "authorites" and right = "authorities" + or + wrong = "authorithy" and right = "authority" + or + wrong = "authoritiers" and right = "authorities" + or + wrong = "authoritive" and right = "authoritative" + or + wrong = "authrorities" and right = "authorities" + or + wrong = "autochtonous" and right = "autochthonous" + or + wrong = "autoctonous" and right = "autochthonous" + or + wrong = "automaticly" and right = "automatically" + or + wrong = "automibile" and right = "automobile" + or + wrong = "automonomous" and right = "autonomous" + or + wrong = "autor" and right = "author" + or + wrong = "autority" and right = "authority" + or + wrong = "auxilary" and right = "auxiliary" + or + wrong = "auxillaries" and right = "auxiliaries" + or + wrong = "auxillary" and right = "auxiliary" + or + wrong = "auxilliaries" and right = "auxiliaries" + or + wrong = "auxilliary" and right = "auxiliary" + or + wrong = "availabe" and right = "available" + or + wrong = "availablity" and right = "availability" + or + wrong = "availaible" and right = "available" + or + wrong = "availble" and right = "available" + or + wrong = "availiable" and right = "available" + or + wrong = "availible" and right = "available" + or + wrong = "avalable" and right = "available" + or + wrong = "avalance" and right = "avalanche" + or + wrong = "avaliable" and right = "available" + or + wrong = "avation" and right = "aviation" + or + wrong = "averageed" and right = "averaged" + or + wrong = "avilable" and right = "available" + or + wrong = "awared" and right = "awarded" + or + wrong = "awya" and right = "away" + or + wrong = "baceause" and right = "because" + or + wrong = "backgorund" and right = "background" + or + wrong = "backrounds" and right = "backgrounds" + or + wrong = "bakc" and right = "back" + or + wrong = "banannas" and right = "bananas" + or + wrong = "bandwith" and right = "bandwidth" + or + wrong = "bankrupcy" and right = "bankruptcy" + or + wrong = "banruptcy" and right = "bankruptcy" + or + wrong = "baout" and right = "about" + or + wrong = "baout" and right = "bout" + or + wrong = "basicaly" and right = "basically" + or + wrong = "basicly" and right = "basically" + or + wrong = "bcak" and right = "back" + or + wrong = "beachead" and right = "beachhead" + or + wrong = "beacuse" and right = "because" + or + wrong = "beastiality" and right = "bestiality" + or + wrong = "beatiful" and right = "beautiful" + or + wrong = "beaurocracy" and right = "bureaucracy" + or + wrong = "beaurocratic" and right = "bureaucratic" + or + wrong = "beautyfull" and right = "beautiful" + or + wrong = "becamae" and right = "became" + or + wrong = "becames" and right = "became" + or + wrong = "becames" and right = "becomes" + or + wrong = "becasue" and right = "because" + or + wrong = "beccause" and right = "because" + or + wrong = "becomeing" and right = "becoming" + or + wrong = "becomming" and right = "becoming" + or + wrong = "becouse" and right = "because" + or + wrong = "becuase" and right = "because" + or + wrong = "bedore" and right = "before" + or + wrong = "beeing" and right = "being" + or + wrong = "befoer" and right = "before" + or + wrong = "beggin" and right = "begging" + or + wrong = "beggin" and right = "begin" + or + wrong = "begginer" and right = "beginner" + or + wrong = "begginers" and right = "beginners" + or + wrong = "beggining" and right = "beginning" + or + wrong = "begginings" and right = "beginnings" + or + wrong = "beggins" and right = "begins" + or + wrong = "begining" and right = "beginning" + or + wrong = "beginnig" and right = "beginning" + or + wrong = "behavour" and right = "behavior" + or + wrong = "behavour" and right = "behaviour" + or + wrong = "beleagured" and right = "beleaguered" + or + wrong = "beleif" and right = "belief" + or + wrong = "beleive" and right = "believe" + or + wrong = "beleived" and right = "believed" + or + wrong = "beleives" and right = "believes" + or + wrong = "beleiving" and right = "believing" + or + wrong = "beligum" and right = "belgium" + or + wrong = "belive" and right = "believe" + or + wrong = "belived" and right = "believed" + or + wrong = "belived" and right = "beloved" + or + wrong = "belives" and right = "beliefs" + or + wrong = "belives" and right = "believes" + or + wrong = "belligerant" and right = "belligerent" + or + wrong = "bellweather" and right = "bellwether" + or + wrong = "bemusemnt" and right = "bemusement" + or + wrong = "beneficary" and right = "beneficiary" + or + wrong = "beng" and right = "being" + or + wrong = "benificial" and right = "beneficial" + or + wrong = "benifit" and right = "benefit" + or + wrong = "benifits" and right = "benefits" + or + wrong = "bergamont" and right = "bergamot" + or + wrong = "bernouilli" and right = "bernoulli" + or + wrong = "beseige" and right = "besiege" + or + wrong = "beseiged" and right = "besieged" + or + wrong = "beseiging" and right = "besieging" + or + wrong = "beteen" and right = "between" + or + wrong = "betwen" and right = "between" + or + wrong = "beween" and right = "between" + or + wrong = "bewteen" and right = "between" + or + wrong = "bigining" and right = "beginning" + or + wrong = "biginning" and right = "beginning" + or + wrong = "bilateraly" and right = "bilaterally" + or + wrong = "billingualism" and right = "bilingualism" + or + wrong = "binominal" and right = "binomial" + or + wrong = "bizzare" and right = "bizarre" + or + wrong = "blaim" and right = "blame" + or + wrong = "blaimed" and right = "blamed" + or + wrong = "blessure" and right = "blessing" + or + wrong = "blitzkreig" and right = "blitzkrieg" + or + wrong = "boaut" and right = "about" + or + wrong = "boaut" and right = "boat" + or + wrong = "boaut" and right = "bout" + or + wrong = "bodydbuilder" and right = "bodybuilder" + or + wrong = "bolean" and right = "boolean" + or + wrong = "bombardement" and right = "bombardment" + or + wrong = "bombarment" and right = "bombardment" + or + wrong = "bondary" and right = "boundary" + or + wrong = "bonnano" and right = "bonanno" + or + wrong = "boook" and right = "book" + or + wrong = "borke" and right = "broke" + or + wrong = "boundry" and right = "boundary" + or + wrong = "bouyancy" and right = "buoyancy" + or + wrong = "bouyant" and right = "buoyant" + or + wrong = "boyant" and right = "buoyant" + or + wrong = "bradcast" and right = "broadcast" + or + wrong = "brasillian" and right = "brazilian" + or + wrong = "breakthough" and right = "breakthrough" + or + wrong = "breakthroughts" and right = "breakthroughs" + or + wrong = "breif" and right = "brief" + or + wrong = "breifly" and right = "briefly" + or + wrong = "brethen" and right = "brethren" + or + wrong = "bretheren" and right = "brethren" + or + wrong = "briliant" and right = "brilliant" + or + wrong = "brillant" and right = "brilliant" + or + wrong = "brimestone" and right = "brimstone" + or + wrong = "britian" and right = "britain" + or + wrong = "brittish" and right = "british" + or + wrong = "broacasted" and right = "broadcast" + or + wrong = "broadacasting" and right = "broadcasting" + or + wrong = "broady" and right = "broadly" + or + wrong = "buddah" and right = "buddha" + or + wrong = "buddist" and right = "buddhist" + or + wrong = "buisness" and right = "business" + or + wrong = "buisnessman" and right = "businessman" + or + wrong = "buoancy" and right = "buoyancy" + or + wrong = "buring" and right = "burin" + or + wrong = "buring" and right = "burning" + or + wrong = "buring" and right = "burying" + or + wrong = "buring" and right = "during" + or + wrong = "burried" and right = "buried" + or + wrong = "busines" and right = "business" + or + wrong = "busineses" and right = "business" + or + wrong = "busineses" and right = "businesses" + or + wrong = "busness" and right = "business" + or + wrong = "bussiness" and right = "business" + or + wrong = "caculater" and right = "calculator" + or + wrong = "cacuses" and right = "caucuses" + or + wrong = "cahracters" and right = "characters" + or + wrong = "calaber" and right = "caliber" + or + wrong = "calander" and right = "calendar" + or + wrong = "calander" and right = "calender" + or + wrong = "calander" and right = "colander" + or + wrong = "calculater" and right = "calculator" + or + wrong = "calculs" and right = "calculus" + or + wrong = "calender" and right = "calendar" + or + wrong = "calenders" and right = "calendars" + or + wrong = "caligraphy" and right = "calligraphy" + or + wrong = "caluclate" and right = "calculate" + or + wrong = "caluclated" and right = "calculated" + or + wrong = "caluculate" and right = "calculate" + or + wrong = "caluculated" and right = "calculated" + or + wrong = "calulate" and right = "calculate" + or + wrong = "calulated" and right = "calculated" + or + wrong = "calulater" and right = "calculator" + or + wrong = "cambrige" and right = "cambridge" + or + wrong = "camoflage" and right = "camouflage" + or + wrong = "campagin" and right = "campaign" + or + wrong = "campain" and right = "campaign" + or + wrong = "campains" and right = "campaigns" + or + wrong = "candadate" and right = "candidate" + or + wrong = "candiate" and right = "candidate" + or + wrong = "candidiate" and right = "candidate" + or + wrong = "cannister" and right = "canister" + or + wrong = "cannisters" and right = "canisters" + or + wrong = "cannnot" and right = "cannot" + or + wrong = "cannonical" and right = "canonical" + or + wrong = "cannotation" and right = "connotation" + or + wrong = "cannotations" and right = "connotations" + or + wrong = "caost" and right = "coast" + or + wrong = "caperbility" and right = "capability" + or + wrong = "capible" and right = "capable" + or + wrong = "captial" and right = "capital" + or + wrong = "captued" and right = "captured" + or + wrong = "capturd" and right = "captured" + or + wrong = "carachter" and right = "character" + or + wrong = "caracterized" and right = "characterized" + or + wrong = "carcas" and right = "caracas" + or + wrong = "carcas" and right = "carcass" + or + wrong = "carefull" and right = "careful" + or + wrong = "careing" and right = "caring" + or + wrong = "carismatic" and right = "charismatic" + or + wrong = "carmalite" and right = "carmelite" + or + wrong = "carnagie" and right = "carnegie" + or + wrong = "carnege" and right = "carnage" + or + wrong = "carnege" and right = "carnegie" + or + wrong = "carnige" and right = "carnage" + or + wrong = "carnige" and right = "carnegie" + or + wrong = "carnigie" and right = "carnegie" + or + wrong = "carreer" and right = "career" + or + wrong = "carrers" and right = "careers" + or + wrong = "carribbean" and right = "caribbean" + or + wrong = "carribean" and right = "caribbean" + or + wrong = "carryng" and right = "carrying" + or + wrong = "cartdridge" and right = "cartridge" + or + wrong = "carthagian" and right = "carthaginian" + or + wrong = "carthographer" and right = "cartographer" + or + wrong = "cartilege" and right = "cartilage" + or + wrong = "cartilidge" and right = "cartilage" + or + wrong = "cartrige" and right = "cartridge" + or + wrong = "casette" and right = "cassette" + or + wrong = "casion" and right = "caisson" + or + wrong = "cassawory" and right = "cassowary" + or + wrong = "cassowarry" and right = "cassowary" + or + wrong = "casue" and right = "cause" + or + wrong = "casued" and right = "caused" + or + wrong = "casues" and right = "causes" + or + wrong = "casuing" and right = "causing" + or + wrong = "casulaties" and right = "casualties" + or + wrong = "casulaty" and right = "casualty" + or + wrong = "catagories" and right = "categories" + or + wrong = "catagorized" and right = "categorized" + or + wrong = "catagory" and right = "category" + or + wrong = "cataline" and right = "catalina" + or + wrong = "cataline" and right = "catiline" + or + wrong = "catapillar" and right = "caterpillar" + or + wrong = "catapillars" and right = "caterpillars" + or + wrong = "catapiller" and right = "caterpillar" + or + wrong = "catapillers" and right = "caterpillars" + or + wrong = "catepillar" and right = "caterpillar" + or + wrong = "catepillars" and right = "caterpillars" + or + wrong = "catergorize" and right = "categorize" + or + wrong = "catergorized" and right = "categorized" + or + wrong = "caterpilar" and right = "caterpillar" + or + wrong = "caterpilars" and right = "caterpillars" + or + wrong = "caterpiller" and right = "caterpillar" + or + wrong = "caterpillers" and right = "caterpillars" + or + wrong = "cathlic" and right = "catholic" + or + wrong = "catholocism" and right = "catholicism" + or + wrong = "catterpilar" and right = "caterpillar" + or + wrong = "catterpilars" and right = "caterpillars" + or + wrong = "catterpillar" and right = "caterpillar" + or + wrong = "catterpillars" and right = "caterpillars" + or + wrong = "cattleship" and right = "battleship" + or + wrong = "causalities" and right = "casualties" + or + wrong = "ceasar" and right = "caesar" + or + wrong = "celcius" and right = "celsius" + or + wrong = "cellpading" and right = "cellpadding" + or + wrong = "cementary" and right = "cemetery" + or + wrong = "cemetarey" and right = "cemetery" + or + wrong = "cemetaries" and right = "cemeteries" + or + wrong = "cemetary" and right = "cemetery" + or + wrong = "cencus" and right = "census" + or + wrong = "censur" and right = "censor" + or + wrong = "censur" and right = "censure" + or + wrong = "cententenial" and right = "centennial" + or + wrong = "centruies" and right = "centuries" + or + wrong = "centruy" and right = "century" + or + wrong = "centuties" and right = "centuries" + or + wrong = "centuty" and right = "century" + or + wrong = "ceratin" and right = "certain" + or + wrong = "ceratin" and right = "keratin" + or + wrong = "cerimonial" and right = "ceremonial" + or + wrong = "cerimonies" and right = "ceremonies" + or + wrong = "cerimonious" and right = "ceremonious" + or + wrong = "cerimony" and right = "ceremony" + or + wrong = "ceromony" and right = "ceremony" + or + wrong = "certainity" and right = "certainty" + or + wrong = "certian" and right = "certain" + or + wrong = "cervial" and right = "cervical" + or + wrong = "cervial" and right = "serval" + or + wrong = "cervial" and right = "servile" + or + wrong = "chalenging" and right = "challenging" + or + wrong = "challange" and right = "challenge" + or + wrong = "challanged" and right = "challenged" + or + wrong = "challege" and right = "challenge" + or + wrong = "champange" and right = "champagne" + or + wrong = "changable" and right = "changeable" + or + wrong = "charachter" and right = "character" + or + wrong = "charachters" and right = "characters" + or + wrong = "charactersistic" and right = "characteristic" + or + wrong = "charactor" and right = "character" + or + wrong = "charactors" and right = "characters" + or + wrong = "charasmatic" and right = "charismatic" + or + wrong = "charaterized" and right = "characterized" + or + wrong = "chariman" and right = "chairman" + or + wrong = "charistics" and right = "characteristics" + or + wrong = "chasr" and right = "chase" + or + wrong = "chasr" and right = "chaser" + or + wrong = "cheif" and right = "chief" + or + wrong = "cheifs" and right = "chiefs" + or + wrong = "chemcial" and right = "chemical" + or + wrong = "chemcially" and right = "chemically" + or + wrong = "chemestry" and right = "chemistry" + or + wrong = "chemicaly" and right = "chemically" + or + wrong = "childbird" and right = "childbirth" + or + wrong = "childen" and right = "children" + or + wrong = "choclate" and right = "chocolate" + or + wrong = "choosen" and right = "chosen" + or + wrong = "chracter" and right = "character" + or + wrong = "chuch" and right = "church" + or + wrong = "churchs" and right = "churches" + or + wrong = "cincinatti" and right = "cincinnati" + or + wrong = "cincinnatti" and right = "cincinnati" + or + wrong = "circulaton" and right = "circulation" + or + wrong = "circumsicion" and right = "circumcision" + or + wrong = "circut" and right = "circuit" + or + wrong = "ciricuit" and right = "circuit" + or + wrong = "ciriculum" and right = "curriculum" + or + wrong = "civillian" and right = "civilian" + or + wrong = "claer" and right = "clear" + or + wrong = "claerer" and right = "clearer" + or + wrong = "claerly" and right = "clearly" + or + wrong = "claimes" and right = "claims" + or + wrong = "clas" and right = "class" + or + wrong = "clasic" and right = "classic" + or + wrong = "clasical" and right = "classical" + or + wrong = "clasically" and right = "classically" + or + wrong = "cleareance" and right = "clearance" + or + wrong = "clera" and right = "clear" + or + wrong = "clera" and right = "sclera" + or + wrong = "clincial" and right = "clinical" + or + wrong = "clinicaly" and right = "clinically" + or + wrong = "cmo" and right = "com" + or + wrong = "cmoputer" and right = "computer" + or + wrong = "coctail" and right = "cocktail" + or + wrong = "coform" and right = "conform" + or + wrong = "cognizent" and right = "cognizant" + or + wrong = "coincedentally" and right = "coincidentally" + or + wrong = "colaborations" and right = "collaborations" + or + wrong = "colateral" and right = "collateral" + or + wrong = "colelctive" and right = "collective" + or + wrong = "collaberative" and right = "collaborative" + or + wrong = "collecton" and right = "collection" + or + wrong = "collegue" and right = "colleague" + or + wrong = "collegues" and right = "colleagues" + or + wrong = "collonade" and right = "colonnade" + or + wrong = "collonies" and right = "colonies" + or + wrong = "collony" and right = "colony" + or + wrong = "collosal" and right = "colossal" + or + wrong = "colonizators" and right = "colonizers" + or + wrong = "comander" and right = "commandeer" + or + wrong = "comander" and right = "commander" + or + wrong = "comando" and right = "commando" + or + wrong = "comandos" and right = "commandos" + or + wrong = "comany" and right = "company" + or + wrong = "comapany" and right = "company" + or + wrong = "comback" and right = "comeback" + or + wrong = "combanations" and right = "combinations" + or + wrong = "combinatins" and right = "combinations" + or + wrong = "combusion" and right = "combustion" + or + wrong = "comdemnation" and right = "condemnation" + or + wrong = "comemmorates" and right = "commemorates" + or + wrong = "comemoretion" and right = "commemoration" + or + wrong = "comision" and right = "commission" + or + wrong = "comisioned" and right = "commissioned" + or + wrong = "comisioner" and right = "commissioner" + or + wrong = "comisioning" and right = "commissioning" + or + wrong = "comisions" and right = "commissions" + or + wrong = "comission" and right = "commission" + or + wrong = "comissioned" and right = "commissioned" + or + wrong = "comissioner" and right = "commissioner" + or + wrong = "comissioning" and right = "commissioning" + or + wrong = "comissions" and right = "commissions" + or + wrong = "comited" and right = "committed" + or + wrong = "comiting" and right = "committing" + or + wrong = "comitted" and right = "committed" + or + wrong = "comittee" and right = "committee" + or + wrong = "comitting" and right = "committing" + or + wrong = "commandoes" and right = "commandos" + or + wrong = "commedic" and right = "comedic" + or + wrong = "commemerative" and right = "commemorative" + or + wrong = "commemmorate" and right = "commemorate" + or + wrong = "commemmorating" and right = "commemorating" + or + wrong = "commerical" and right = "commercial" + or + wrong = "commerically" and right = "commercially" + or + wrong = "commericial" and right = "commercial" + or + wrong = "commericially" and right = "commercially" + or + wrong = "commerorative" and right = "commemorative" + or + wrong = "comming" and right = "coming" + or + wrong = "comminication" and right = "communication" + or + wrong = "commision" and right = "commission" + or + wrong = "commisioned" and right = "commissioned" + or + wrong = "commisioner" and right = "commissioner" + or + wrong = "commisioning" and right = "commissioning" + or + wrong = "commisions" and right = "commissions" + or + wrong = "commited" and right = "committed" + or + wrong = "commitee" and right = "committee" + or + wrong = "commiting" and right = "committing" + or + wrong = "committe" and right = "committee" + or + wrong = "committment" and right = "commitment" + or + wrong = "committments" and right = "commitments" + or + wrong = "commmemorated" and right = "commemorated" + or + wrong = "commongly" and right = "commonly" + or + wrong = "commonweath" and right = "commonwealth" + or + wrong = "commuications" and right = "communications" + or + wrong = "commuinications" and right = "communications" + or + wrong = "communciation" and right = "communication" + or + wrong = "communiation" and right = "communication" + or + wrong = "communites" and right = "communities" + or + wrong = "compability" and right = "compatibility" + or + wrong = "comparision" and right = "comparison" + or + wrong = "comparisions" and right = "comparisons" + or + wrong = "comparitive" and right = "comparative" + or + wrong = "comparitively" and right = "comparatively" + or + wrong = "compatabilities" and right = "compatibilities" + or + wrong = "compatability" and right = "compatibility" + or + wrong = "compatable" and right = "compatible" + or + wrong = "compatablities" and right = "compatibilities" + or + wrong = "compatablity" and right = "compatibility" + or + wrong = "compatiable" and right = "compatible" + or + wrong = "compatiblities" and right = "compatibilities" + or + wrong = "compatiblity" and right = "compatibility" + or + wrong = "compeitions" and right = "competitions" + or + wrong = "compensantion" and right = "compensation" + or + wrong = "competance" and right = "competence" + or + wrong = "competant" and right = "competent" + or + wrong = "competative" and right = "competitive" + or + wrong = "competion" and right = "competition" + or + wrong = "competion" and right = "completion" + or + wrong = "competitiion" and right = "competition" + or + wrong = "competive" and right = "competitive" + or + wrong = "competiveness" and right = "competitiveness" + or + wrong = "comphrehensive" and right = "comprehensive" + or + wrong = "compitent" and right = "competent" + or + wrong = "completelyl" and right = "completely" + or + wrong = "completetion" and right = "completion" + or + wrong = "complier" and right = "compiler" + or + wrong = "componant" and right = "component" + or + wrong = "comprable" and right = "comparable" + or + wrong = "comprimise" and right = "compromise" + or + wrong = "compulsary" and right = "compulsory" + or + wrong = "compulsery" and right = "compulsory" + or + wrong = "computarized" and right = "computerized" + or + wrong = "concensus" and right = "consensus" + or + wrong = "concider" and right = "consider" + or + wrong = "concidered" and right = "considered" + or + wrong = "concidering" and right = "considering" + or + wrong = "conciders" and right = "considers" + or + wrong = "concieted" and right = "conceited" + or + wrong = "concieved" and right = "conceived" + or + wrong = "concious" and right = "conscious" + or + wrong = "conciously" and right = "consciously" + or + wrong = "conciousness" and right = "consciousness" + or + wrong = "condamned" and right = "condemned" + or + wrong = "condemmed" and right = "condemned" + or + wrong = "condidtion" and right = "condition" + or + wrong = "condidtions" and right = "conditions" + or + wrong = "conected" and right = "connected" + or + wrong = "conection" and right = "connection" + or + wrong = "conesencus" and right = "consensus" + or + wrong = "confidental" and right = "confidential" + or + wrong = "confidentally" and right = "confidentially" + or + wrong = "confids" and right = "confides" + or + wrong = "configureable" and right = "configurable" + or + wrong = "confortable" and right = "comfortable" + or + wrong = "congradulations" and right = "congratulations" + or + wrong = "congresional" and right = "congressional" + or + wrong = "conived" and right = "connived" + or + wrong = "conjecutre" and right = "conjecture" + or + wrong = "conjuction" and right = "conjunction" + or + wrong = "connectinos" and right = "connections" + or + wrong = "conneticut" and right = "connecticut" + or + wrong = "conotations" and right = "connotations" + or + wrong = "conquerd" and right = "conquered" + or + wrong = "conquerer" and right = "conqueror" + or + wrong = "conquerers" and right = "conquerors" + or + wrong = "conqured" and right = "conquered" + or + wrong = "conscent" and right = "consent" + or + wrong = "consciouness" and right = "consciousness" + or + wrong = "consdider" and right = "consider" + or + wrong = "consdidered" and right = "considered" + or + wrong = "consdiered" and right = "considered" + or + wrong = "consectutive" and right = "consecutive" + or + wrong = "consenquently" and right = "consequently" + or + wrong = "consentrate" and right = "concentrate" + or + wrong = "consentrated" and right = "concentrated" + or + wrong = "consentrates" and right = "concentrates" + or + wrong = "consept" and right = "concept" + or + wrong = "consequentually" and right = "consequently" + or + wrong = "consequeseces" and right = "consequences" + or + wrong = "consern" and right = "concern" + or + wrong = "conserned" and right = "concerned" + or + wrong = "conserning" and right = "concerning" + or + wrong = "conservitive" and right = "conservative" + or + wrong = "consiciousness" and right = "consciousness" + or + wrong = "consicousness" and right = "consciousness" + or + wrong = "considerd" and right = "considered" + or + wrong = "consideres" and right = "considered" + or + wrong = "consious" and right = "conscious" + or + wrong = "consistant" and right = "consistent" + or + wrong = "consistantly" and right = "consistently" + or + wrong = "consituencies" and right = "constituencies" + or + wrong = "consituency" and right = "constituency" + or + wrong = "consituted" and right = "constituted" + or + wrong = "consitution" and right = "constitution" + or + wrong = "consitutional" and right = "constitutional" + or + wrong = "consolodate" and right = "consolidate" + or + wrong = "consolodated" and right = "consolidated" + or + wrong = "consonent" and right = "consonant" + or + wrong = "consonents" and right = "consonants" + or + wrong = "consorcium" and right = "consortium" + or + wrong = "conspiracys" and right = "conspiracies" + or + wrong = "conspiriator" and right = "conspirator" + or + wrong = "consructor" and right = "constructor" + or + wrong = "constaints" and right = "constraints" + or + wrong = "constanly" and right = "constantly" + or + wrong = "constarnation" and right = "consternation" + or + wrong = "constatn" and right = "constant" + or + wrong = "constinually" and right = "continually" + or + wrong = "constituant" and right = "constituent" + or + wrong = "constituants" and right = "constituents" + or + wrong = "constituion" and right = "constitution" + or + wrong = "constituional" and right = "constitutional" + or + wrong = "consttruction" and right = "construction" + or + wrong = "constuction" and right = "construction" + or + wrong = "consulant" and right = "consultant" + or + wrong = "consumate" and right = "consummate" + or + wrong = "consumated" and right = "consummated" + or + wrong = "contaiminate" and right = "contaminate" + or + wrong = "containes" and right = "contains" + or + wrong = "contamporaries" and right = "contemporaries" + or + wrong = "contamporary" and right = "contemporary" + or + wrong = "contempoary" and right = "contemporary" + or + wrong = "contemporaneus" and right = "contemporaneous" + or + wrong = "contempory" and right = "contemporary" + or + wrong = "contendor" and right = "contender" + or + wrong = "contian" and right = "contain" + or + wrong = "contians" and right = "contains" + or + wrong = "contibute" and right = "contribute" + or + wrong = "contibuted" and right = "contributed" + or + wrong = "contibutes" and right = "contributes" + or + wrong = "contigent" and right = "contingent" + or + wrong = "contined" and right = "continued" + or + wrong = "continential" and right = "continental" + or + wrong = "continous" and right = "continuous" + or + wrong = "continously" and right = "continuously" + or + wrong = "continueing" and right = "continuing" + or + wrong = "contravercial" and right = "controversial" + or + wrong = "contraversy" and right = "controversy" + or + wrong = "contributer" and right = "contributor" + or + wrong = "contributers" and right = "contributors" + or + wrong = "contritutions" and right = "contributions" + or + wrong = "controled" and right = "controlled" + or + wrong = "controling" and right = "controlling" + or + wrong = "controll" and right = "control" + or + wrong = "controlls" and right = "controls" + or + wrong = "controvercial" and right = "controversial" + or + wrong = "controvercy" and right = "controversy" + or + wrong = "controveries" and right = "controversies" + or + wrong = "controversal" and right = "controversial" + or + wrong = "controversey" and right = "controversy" + or + wrong = "controvertial" and right = "controversial" + or + wrong = "controvery" and right = "controversy" + or + wrong = "contruction" and right = "construction" + or + wrong = "contructor" and right = "constructor" + or + wrong = "contstruction" and right = "construction" + or + wrong = "conveinent" and right = "convenient" + or + wrong = "convenant" and right = "covenant" + or + wrong = "convential" and right = "conventional" + or + wrong = "convertables" and right = "convertibles" + or + wrong = "convertion" and right = "conversion" + or + wrong = "conviced" and right = "convinced" + or + wrong = "convienient" and right = "convenient" + or + wrong = "coordiantion" and right = "coordination" + or + wrong = "coorperation" and right = "cooperation" + or + wrong = "coorperation" and right = "corporation" + or + wrong = "coorperations" and right = "corporations" + or + wrong = "copmetitors" and right = "competitors" + or + wrong = "coputer" and right = "computer" + or + wrong = "copywrite" and right = "copyright" + or + wrong = "coridal" and right = "cordial" + or + wrong = "cornmitted" and right = "committed" + or + wrong = "corosion" and right = "corrosion" + or + wrong = "corparate" and right = "corporate" + or + wrong = "corperations" and right = "corporations" + or + wrong = "correcters" and right = "correctors" + or + wrong = "correponding" and right = "corresponding" + or + wrong = "correposding" and right = "corresponding" + or + wrong = "correspondant" and right = "correspondent" + or + wrong = "correspondants" and right = "correspondents" + or + wrong = "corridoors" and right = "corridors" + or + wrong = "corrispond" and right = "correspond" + or + wrong = "corrispondant" and right = "correspondent" + or + wrong = "corrispondants" and right = "correspondents" + or + wrong = "corrisponded" and right = "corresponded" + or + wrong = "corrisponding" and right = "corresponding" + or + wrong = "corrisponds" and right = "corresponds" + or + wrong = "costitution" and right = "constitution" + or + wrong = "coucil" and right = "council" + or + wrong = "coudl" and right = "cloud" + or + wrong = "coudl" and right = "could" + or + wrong = "councellor" and right = "councillor" + or + wrong = "councellor" and right = "councilor" + or + wrong = "councellor" and right = "counselor" + or + wrong = "councellors" and right = "councillors" + or + wrong = "councellors" and right = "councilors" + or + wrong = "councellors" and right = "counselors" + or + wrong = "counries" and right = "countries" + or + wrong = "countains" and right = "contains" + or + wrong = "countires" and right = "countries" + or + wrong = "coururier" and right = "courier" + or + wrong = "coururier" and right = "couturier" + or + wrong = "coverted" and right = "converted" + or + wrong = "coverted" and right = "covered" + or + wrong = "coverted" and right = "coveted" + or + wrong = "cpoy" and right = "copy" + or + wrong = "cpoy" and right = "coy" + or + wrong = "creaeted" and right = "created" + or + wrong = "creedence" and right = "credence" + or + wrong = "critereon" and right = "criterion" + or + wrong = "criterias" and right = "criteria" + or + wrong = "criticists" and right = "critics" + or + wrong = "critising" and right = "criticising" + or + wrong = "critising" and right = "criticizing" + or + wrong = "critisising" and right = "criticising" + or + wrong = "critisism" and right = "criticism" + or + wrong = "critisisms" and right = "criticisms" + or + wrong = "critisize" and right = "criticise" + or + wrong = "critisize" and right = "criticize" + or + wrong = "critisized" and right = "criticised" + or + wrong = "critisized" and right = "criticized" + or + wrong = "critisizes" and right = "criticises" + or + wrong = "critisizes" and right = "criticizes" + or + wrong = "critisizing" and right = "criticising" + or + wrong = "critisizing" and right = "criticizing" + or + wrong = "critized" and right = "criticized" + or + wrong = "critizing" and right = "criticizing" + or + wrong = "crockodiles" and right = "crocodiles" + or + wrong = "crowm" and right = "crown" + or + wrong = "crtical" and right = "critical" + or + wrong = "crticised" and right = "criticised" + or + wrong = "crucifiction" and right = "crucifixion" + or + wrong = "crusies" and right = "cruises" + or + wrong = "crutial" and right = "crucial" + or + wrong = "crystalisation" and right = "crystallisation" + or + wrong = "culiminating" and right = "culminating" + or + wrong = "cumulatative" and right = "cumulative" + or + wrong = "curch" and right = "church" + or + wrong = "curcuit" and right = "circuit" + or + wrong = "currenly" and right = "currently" + or + wrong = "curriculem" and right = "curriculum" + or + wrong = "cxan" and right = "cyan" + or + wrong = "cyclinder" and right = "cylinder" + or + wrong = "dacquiri" and right = "daiquiri" + or + wrong = "daed" and right = "dead" + or + wrong = "dael" and right = "dahl" + or + wrong = "dael" and right = "deal" + or + wrong = "dael" and right = "dial" + or + wrong = "dalmation" and right = "dalmatian" + or + wrong = "damenor" and right = "demeanor" + or + wrong = "dammage" and right = "damage" + or + wrong = "dardenelles" and right = "dardanelles" + or + wrong = "daugher" and right = "daughter" + or + wrong = "deafult" and right = "default" + or + wrong = "debateable" and right = "debatable" + or + wrong = "decendant" and right = "descendant" + or + wrong = "decendants" and right = "descendants" + or + wrong = "decendent" and right = "descendant" + or + wrong = "decendents" and right = "descendants" + or + wrong = "decideable" and right = "decidable" + or + wrong = "decidely" and right = "decidedly" + or + wrong = "decieved" and right = "deceived" + or + wrong = "decison" and right = "decision" + or + wrong = "decomissioned" and right = "decommissioned" + or + wrong = "decomposit" and right = "decompose" + or + wrong = "decomposited" and right = "decomposed" + or + wrong = "decompositing" and right = "decomposing" + or + wrong = "decomposits" and right = "decomposes" + or + wrong = "decress" and right = "decrees" + or + wrong = "decribe" and right = "describe" + or + wrong = "decribed" and right = "described" + or + wrong = "decribes" and right = "describes" + or + wrong = "decribing" and right = "describing" + or + wrong = "dectect" and right = "detect" + or + wrong = "defendent" and right = "defendant" + or + wrong = "defendents" and right = "defendants" + or + wrong = "deffensively" and right = "defensively" + or + wrong = "deffine" and right = "define" + or + wrong = "deffined" and right = "defined" + or + wrong = "definance" and right = "defiance" + or + wrong = "definate" and right = "definite" + or + wrong = "definately" and right = "definitely" + or + wrong = "definatly" and right = "definitely" + or + wrong = "definetly" and right = "definitely" + or + wrong = "definining" and right = "defining" + or + wrong = "definit" and right = "definite" + or + wrong = "definitly" and right = "definitely" + or + wrong = "definiton" and right = "definition" + or + wrong = "defintion" and right = "definition" + or + wrong = "defualt" and right = "default" + or + wrong = "defult" and right = "default" + or + wrong = "degrate" and right = "degrade" + or + wrong = "delagates" and right = "delegates" + or + wrong = "delapidated" and right = "dilapidated" + or + wrong = "delerious" and right = "delirious" + or + wrong = "delevopment" and right = "development" + or + wrong = "deliberatly" and right = "deliberately" + or + wrong = "delusionally" and right = "delusively" + or + wrong = "demenor" and right = "demeanor" + or + wrong = "demographical" and right = "demographic" + or + wrong = "demolision" and right = "demolition" + or + wrong = "demorcracy" and right = "democracy" + or + wrong = "demostration" and right = "demonstration" + or + wrong = "denegrating" and right = "denigrating" + or + wrong = "densly" and right = "densely" + or + wrong = "deparment" and right = "department" + or + wrong = "deparmental" and right = "departmental" + or + wrong = "deparments" and right = "departments" + or + wrong = "dependance" and right = "dependence" + or + wrong = "dependancy" and right = "dependency" + or + wrong = "deram" and right = "dram" + or + wrong = "deram" and right = "dream" + or + wrong = "deriviated" and right = "derived" + or + wrong = "derivitive" and right = "derivative" + or + wrong = "derogitory" and right = "derogatory" + or + wrong = "descendands" and right = "descendants" + or + wrong = "descibed" and right = "described" + or + wrong = "desciptors" and right = "descriptors" + or + wrong = "descision" and right = "decision" + or + wrong = "descisions" and right = "decisions" + or + wrong = "descriibes" and right = "describes" + or + wrong = "descripters" and right = "descriptors" + or + wrong = "descripton" and right = "description" + or + wrong = "desctruction" and right = "destruction" + or + wrong = "descuss" and right = "discuss" + or + wrong = "desgined" and right = "designed" + or + wrong = "deside" and right = "decide" + or + wrong = "desigining" and right = "designing" + or + wrong = "desinations" and right = "destinations" + or + wrong = "desintegrated" and right = "disintegrated" + or + wrong = "desintegration" and right = "disintegration" + or + wrong = "desireable" and right = "desirable" + or + wrong = "desitned" and right = "destined" + or + wrong = "desktiop" and right = "desktop" + or + wrong = "desorder" and right = "disorder" + or + wrong = "desoriented" and right = "disoriented" + or + wrong = "desparate" and right = "desperate" + or + wrong = "desparate" and right = "disparate" + or + wrong = "despict" and right = "depict" + or + wrong = "despiration" and right = "desperation" + or + wrong = "dessicated" and right = "desiccated" + or + wrong = "dessigned" and right = "designed" + or + wrong = "destablized" and right = "destabilized" + or + wrong = "destory" and right = "destroy" + or + wrong = "desugered" and right = "desugared" + or + wrong = "detailled" and right = "detailed" + or + wrong = "detatched" and right = "detached" + or + wrong = "deteoriated" and right = "deteriorated" + or + wrong = "deteriate" and right = "deteriorate" + or + wrong = "deterioriating" and right = "deteriorating" + or + wrong = "determinining" and right = "determining" + or + wrong = "detremental" and right = "detrimental" + or + wrong = "devasted" and right = "devastated" + or + wrong = "develope" and right = "develop" + or + wrong = "developement" and right = "development" + or + wrong = "developped" and right = "developed" + or + wrong = "develpment" and right = "development" + or + wrong = "devels" and right = "delves" + or + wrong = "devestated" and right = "devastated" + or + wrong = "devestating" and right = "devastating" + or + wrong = "devide" and right = "divide" + or + wrong = "devided" and right = "divided" + or + wrong = "devistating" and right = "devastating" + or + wrong = "devolopement" and right = "development" + or + wrong = "diablical" and right = "diabolical" + or + wrong = "diamons" and right = "diamonds" + or + wrong = "diaster" and right = "disaster" + or + wrong = "dichtomy" and right = "dichotomy" + or + wrong = "diconnects" and right = "disconnects" + or + wrong = "dicover" and right = "discover" + or + wrong = "dicovered" and right = "discovered" + or + wrong = "dicovering" and right = "discovering" + or + wrong = "dicovers" and right = "discovers" + or + wrong = "dicovery" and right = "discovery" + or + wrong = "dictionarys" and right = "dictionaries" + or + wrong = "dicussed" and right = "discussed" + or + wrong = "diea" and right = "die" + or + wrong = "diea" and right = "idea" + or + wrong = "dieing" and right = "dyeing" + or + wrong = "dieing" and right = "dying" + or + wrong = "dieties" and right = "deities" + or + wrong = "diety" and right = "deity" + or + wrong = "diferent" and right = "different" + or + wrong = "diferrent" and right = "different" + or + wrong = "differentiatiations" and right = "differentiations" + or + wrong = "differnt" and right = "different" + or + wrong = "difficulity" and right = "difficulty" + or + wrong = "diffrent" and right = "different" + or + wrong = "dificulties" and right = "difficulties" + or + wrong = "dificulty" and right = "difficulty" + or + wrong = "dimenions" and right = "dimensions" + or + wrong = "dimention" and right = "dimension" + or + wrong = "dimentional" and right = "dimensional" + or + wrong = "dimentions" and right = "dimensions" + or + wrong = "dimesnional" and right = "dimensional" + or + wrong = "diminuitive" and right = "diminutive" + or + wrong = "dimunitive" and right = "diminutive" + or + wrong = "diosese" and right = "diocese" + or + wrong = "diphtong" and right = "diphthong" + or + wrong = "diphtongs" and right = "diphthongs" + or + wrong = "diplomancy" and right = "diplomacy" + or + wrong = "dipthong" and right = "diphthong" + or + wrong = "dipthongs" and right = "diphthongs" + or + wrong = "directoty" and right = "directory" + or + wrong = "dirived" and right = "derived" + or + wrong = "disagreeed" and right = "disagreed" + or + wrong = "disapeared" and right = "disappeared" + or + wrong = "disapointing" and right = "disappointing" + or + wrong = "disappearred" and right = "disappeared" + or + wrong = "disaproval" and right = "disapproval" + or + wrong = "disasterous" and right = "disastrous" + or + wrong = "disatisfaction" and right = "dissatisfaction" + or + wrong = "disatisfied" and right = "dissatisfied" + or + wrong = "disatrous" and right = "disastrous" + or + wrong = "discontentment" and right = "discontent" + or + wrong = "discribe" and right = "describe" + or + wrong = "discribed" and right = "described" + or + wrong = "discribes" and right = "describes" + or + wrong = "discribing" and right = "describing" + or + wrong = "disctinction" and right = "distinction" + or + wrong = "disctinctive" and right = "distinctive" + or + wrong = "disemination" and right = "dissemination" + or + wrong = "disenchanged" and right = "disenchanted" + or + wrong = "disiplined" and right = "disciplined" + or + wrong = "disobediance" and right = "disobedience" + or + wrong = "disobediant" and right = "disobedient" + or + wrong = "disolved" and right = "dissolved" + or + wrong = "disover" and right = "discover" + or + wrong = "dispair" and right = "despair" + or + wrong = "disparingly" and right = "disparagingly" + or + wrong = "dispence" and right = "dispense" + or + wrong = "dispenced" and right = "dispensed" + or + wrong = "dispencing" and right = "dispensing" + or + wrong = "dispicable" and right = "despicable" + or + wrong = "dispite" and right = "despite" + or + wrong = "dispostion" and right = "disposition" + or + wrong = "disproportiate" and right = "disproportionate" + or + wrong = "disputandem" and right = "disputandum" + or + wrong = "disricts" and right = "districts" + or + wrong = "dissagreement" and right = "disagreement" + or + wrong = "dissapear" and right = "disappear" + or + wrong = "dissapearance" and right = "disappearance" + or + wrong = "dissapeared" and right = "disappeared" + or + wrong = "dissapearing" and right = "disappearing" + or + wrong = "dissapears" and right = "disappears" + or + wrong = "dissappear" and right = "disappear" + or + wrong = "dissappears" and right = "disappears" + or + wrong = "dissappointed" and right = "disappointed" + or + wrong = "dissarray" and right = "disarray" + or + wrong = "dissobediance" and right = "disobedience" + or + wrong = "dissobediant" and right = "disobedient" + or + wrong = "dissobedience" and right = "disobedience" + or + wrong = "dissobedient" and right = "disobedient" + or + wrong = "distiction" and right = "distinction" + or + wrong = "distingish" and right = "distinguish" + or + wrong = "distingished" and right = "distinguished" + or + wrong = "distingishes" and right = "distinguishes" + or + wrong = "distingishing" and right = "distinguishing" + or + wrong = "distingquished" and right = "distinguished" + or + wrong = "distrubution" and right = "distribution" + or + wrong = "distruction" and right = "destruction" + or + wrong = "distructive" and right = "destructive" + or + wrong = "ditributed" and right = "distributed" + or + wrong = "diversed" and right = "diverged" + or + wrong = "diversed" and right = "diverse" + or + wrong = "divice" and right = "device" + or + wrong = "divinition" and right = "divination" + or + wrong = "divison" and right = "division" + or + wrong = "divisons" and right = "divisions" + or + wrong = "doccument" and right = "document" + or + wrong = "doccumented" and right = "documented" + or + wrong = "doccuments" and right = "documents" + or + wrong = "docrines" and right = "doctrines" + or + wrong = "doctines" and right = "doctrines" + or + wrong = "documenatry" and right = "documentary" + or + wrong = "doens" and right = "does" + or + wrong = "doign" and right = "doing" + or + wrong = "dominaton" and right = "domination" + or + wrong = "dominent" and right = "dominant" + or + wrong = "dominiant" and right = "dominant" + or + wrong = "donig" and right = "doing" + or + wrong = "doub" and right = "daub" + or + wrong = "doub" and right = "doubt" + or + wrong = "doulbe" and right = "double" + or + wrong = "dowloads" and right = "downloads" + or + wrong = "dramtic" and right = "dramatic" + or + wrong = "draughtman" and right = "draughtsman" + or + wrong = "dravadian" and right = "dravidian" + or + wrong = "dreasm" and right = "dreams" + or + wrong = "driectly" and right = "directly" + or + wrong = "drnik" and right = "drink" + or + wrong = "druming" and right = "drumming" + or + wrong = "drummless" and right = "drumless" + or + wrong = "dum" and right = "dumb" + or + wrong = "dupicate" and right = "duplicate" + or + wrong = "durig" and right = "during" + or + wrong = "durring" and right = "during" + or + wrong = "duting" and right = "during" + or + wrong = "dyas" and right = "dryas" + or + wrong = "eahc" and right = "each" + or + wrong = "ealier" and right = "earlier" + or + wrong = "earlies" and right = "earliest" + or + wrong = "earnt" and right = "earned" + or + wrong = "ecclectic" and right = "eclectic" + or + wrong = "eceonomy" and right = "economy" + or + wrong = "ecidious" and right = "deciduous" + or + wrong = "eclispe" and right = "eclipse" + or + wrong = "ecomonic" and right = "economic" + or + wrong = "ect" and right = "etc" + or + wrong = "editting" and right = "editing" + or + wrong = "eearly" and right = "early" + or + wrong = "efel" and right = "evil" + or + wrong = "effeciency" and right = "efficiency" + or + wrong = "effecient" and right = "efficient" + or + wrong = "effeciently" and right = "efficiently" + or + wrong = "efficency" and right = "efficiency" + or + wrong = "efficent" and right = "efficient" + or + wrong = "efficently" and right = "efficiently" + or + wrong = "efford" and right = "afford" + or + wrong = "efford" and right = "effort" + or + wrong = "effords" and right = "affords" + or + wrong = "effords" and right = "efforts" + or + wrong = "effulence" and right = "effluence" + or + wrong = "eigth" and right = "eight" + or + wrong = "eigth" and right = "eighth" + or + wrong = "eiter" and right = "either" + or + wrong = "elction" and right = "election" + or + wrong = "electic" and right = "eclectic" + or + wrong = "electic" and right = "electric" + or + wrong = "electon" and right = "election" + or + wrong = "electon" and right = "electron" + or + wrong = "electrial" and right = "electrical" + or + wrong = "electricly" and right = "electrically" + or + wrong = "electricty" and right = "electricity" + or + wrong = "elementay" and right = "elementary" + or + wrong = "eleminated" and right = "eliminated" + or + wrong = "eleminating" and right = "eliminating" + or + wrong = "eles" and right = "eels" + or + wrong = "eletricity" and right = "electricity" + or + wrong = "elicided" and right = "elicited" + or + wrong = "eligable" and right = "eligible" + or + wrong = "elimentary" and right = "elementary" + or + wrong = "ellected" and right = "elected" + or + wrong = "elphant" and right = "elephant" + or + wrong = "embarass" and right = "embarrass" + or + wrong = "embarassed" and right = "embarrassed" + or + wrong = "embarassing" and right = "embarrassing" + or + wrong = "embarassment" and right = "embarrassment" + or + wrong = "embargos" and right = "embargoes" + or + wrong = "embarras" and right = "embarrass" + or + wrong = "embarrased" and right = "embarrassed" + or + wrong = "embarrasing" and right = "embarrassing" + or + wrong = "embarrasment" and right = "embarrassment" + or + wrong = "embezelled" and right = "embezzled" + or + wrong = "emblamatic" and right = "emblematic" + or + wrong = "eminate" and right = "emanate" + or + wrong = "eminated" and right = "emanated" + or + wrong = "emision" and right = "emission" + or + wrong = "emited" and right = "emitted" + or + wrong = "emiting" and right = "emitting" + or + wrong = "emition" and right = "emission" + or + wrong = "emition" and right = "emotion" + or + wrong = "emmediately" and right = "immediately" + or + wrong = "emmigrated" and right = "emigrated" + or + wrong = "emmigrated" and right = "immigrated" + or + wrong = "emminent" and right = "eminent" + or + wrong = "emminent" and right = "imminent" + or + wrong = "emminently" and right = "eminently" + or + wrong = "emmisaries" and right = "emissaries" + or + wrong = "emmisarries" and right = "emissaries" + or + wrong = "emmisarry" and right = "emissary" + or + wrong = "emmisary" and right = "emissary" + or + wrong = "emmision" and right = "emission" + or + wrong = "emmisions" and right = "emissions" + or + wrong = "emmited" and right = "emitted" + or + wrong = "emmiting" and right = "emitting" + or + wrong = "emmitted" and right = "emitted" + or + wrong = "emmitting" and right = "emitting" + or + wrong = "emnity" and right = "enmity" + or + wrong = "emperical" and right = "empirical" + or + wrong = "emphaised" and right = "emphasised" + or + wrong = "emphsis" and right = "emphasis" + or + wrong = "emphysyma" and right = "emphysema" + or + wrong = "empirial" and right = "empirical" + or + wrong = "empirial" and right = "imperial" + or + wrong = "emporer" and right = "emperor" + or + wrong = "emprisoned" and right = "imprisoned" + or + wrong = "enameld" and right = "enameled" + or + wrong = "enchancement" and right = "enhancement" + or + wrong = "encouraing" and right = "encouraging" + or + wrong = "encryptiion" and right = "encryption" + or + wrong = "encylopedia" and right = "encyclopedia" + or + wrong = "endevors" and right = "endeavors" + or + wrong = "endevour" and right = "endeavour" + or + wrong = "endig" and right = "ending" + or + wrong = "endolithes" and right = "endoliths" + or + wrong = "enduce" and right = "induce" + or + wrong = "ened" and right = "need" + or + wrong = "enforceing" and right = "enforcing" + or + wrong = "engagment" and right = "engagement" + or + wrong = "engeneer" and right = "engineer" + or + wrong = "engeneering" and right = "engineering" + or + wrong = "engieneer" and right = "engineer" + or + wrong = "engieneers" and right = "engineers" + or + wrong = "enlargment" and right = "enlargement" + or + wrong = "enlargments" and right = "enlargements" + or + wrong = "enlish" and right = "english" + or + wrong = "enlish" and right = "enlist" + or + wrong = "enourmous" and right = "enormous" + or + wrong = "enourmously" and right = "enormously" + or + wrong = "ensconsed" and right = "ensconced" + or + wrong = "entaglements" and right = "entanglements" + or + wrong = "enteratinment" and right = "entertainment" + or + wrong = "enthusiatic" and right = "enthusiastic" + or + wrong = "entitity" and right = "entity" + or + wrong = "entitlied" and right = "entitled" + or + wrong = "entrepeneur" and right = "entrepreneur" + or + wrong = "entrepeneurs" and right = "entrepreneurs" + or + wrong = "enviorment" and right = "environment" + or + wrong = "enviormental" and right = "environmental" + or + wrong = "enviormentally" and right = "environmentally" + or + wrong = "enviorments" and right = "environments" + or + wrong = "enviornment" and right = "environment" + or + wrong = "enviornmental" and right = "environmental" + or + wrong = "enviornmentalist" and right = "environmentalist" + or + wrong = "enviornmentally" and right = "environmentally" + or + wrong = "enviornments" and right = "environments" + or + wrong = "enviroment" and right = "environment" + or + wrong = "enviromental" and right = "environmental" + or + wrong = "enviromentalist" and right = "environmentalist" + or + wrong = "enviromentally" and right = "environmentally" + or + wrong = "enviroments" and right = "environments" + or + wrong = "environemnt" and right = "environment" + or + wrong = "envolutionary" and right = "evolutionary" + or + wrong = "envrionments" and right = "environments" + or + wrong = "enxt" and right = "next" + or + wrong = "epidsodes" and right = "episodes" + or + wrong = "epsiode" and right = "episode" + or + wrong = "equialent" and right = "equivalent" + or + wrong = "equilibium" and right = "equilibrium" + or + wrong = "equilibrum" and right = "equilibrium" + or + wrong = "equiped" and right = "equipped" + or + wrong = "equippment" and right = "equipment" + or + wrong = "equitorial" and right = "equatorial" + or + wrong = "equivelant" and right = "equivalent" + or + wrong = "equivelent" and right = "equivalent" + or + wrong = "equivilant" and right = "equivalent" + or + wrong = "equivilent" and right = "equivalent" + or + wrong = "equivlalent" and right = "equivalent" + or + wrong = "erally" and right = "orally" + or + wrong = "erally" and right = "really" + or + wrong = "eratic" and right = "erratic" + or + wrong = "eratically" and right = "erratically" + or + wrong = "eraticly" and right = "erratically" + or + wrong = "erested" and right = "arrested" + or + wrong = "erested" and right = "erected" + or + wrong = "errupted" and right = "erupted" + or + wrong = "esential" and right = "essential" + or + wrong = "esitmated" and right = "estimated" + or + wrong = "esle" and right = "else" + or + wrong = "especialy" and right = "especially" + or + wrong = "essencial" and right = "essential" + or + wrong = "essense" and right = "essence" + or + wrong = "essentail" and right = "essential" + or + wrong = "essentialy" and right = "essentially" + or + wrong = "essentual" and right = "essential" + or + wrong = "essesital" and right = "essential" + or + wrong = "estabishes" and right = "establishes" + or + wrong = "establising" and right = "establishing" + or + wrong = "ethnocentricm" and right = "ethnocentrism" + or + wrong = "ethose" and right = "ethos" + or + wrong = "ethose" and right = "those" + or + wrong = "europian" and right = "european" + or + wrong = "europians" and right = "europeans" + or + wrong = "eurpean" and right = "european" + or + wrong = "eurpoean" and right = "european" + or + wrong = "evenhtually" and right = "eventually" + or + wrong = "eventally" and right = "eventually" + or + wrong = "eventially" and right = "eventually" + or + wrong = "eventualy" and right = "eventually" + or + wrong = "everthing" and right = "everything" + or + wrong = "everyting" and right = "everything" + or + wrong = "eveyr" and right = "every" + or + wrong = "evidentally" and right = "evidently" + or + wrong = "exagerate" and right = "exaggerate" + or + wrong = "exagerated" and right = "exaggerated" + or + wrong = "exagerates" and right = "exaggerates" + or + wrong = "exagerating" and right = "exaggerating" + or + wrong = "exagerrate" and right = "exaggerate" + or + wrong = "exagerrated" and right = "exaggerated" + or + wrong = "exagerrates" and right = "exaggerates" + or + wrong = "exagerrating" and right = "exaggerating" + or + wrong = "examinated" and right = "examined" + or + wrong = "exampt" and right = "exempt" + or + wrong = "exapansion" and right = "expansion" + or + wrong = "excact" and right = "exact" + or + wrong = "excange" and right = "exchange" + or + wrong = "excecute" and right = "execute" + or + wrong = "excecuted" and right = "executed" + or + wrong = "excecutes" and right = "executes" + or + wrong = "excecuting" and right = "executing" + or + wrong = "excecution" and right = "execution" + or + wrong = "excedded" and right = "exceeded" + or + wrong = "excelent" and right = "excellent" + or + wrong = "excell" and right = "excel" + or + wrong = "excellance" and right = "excellence" + or + wrong = "excellant" and right = "excellent" + or + wrong = "excells" and right = "excels" + or + wrong = "excercise" and right = "exercise" + or + wrong = "exchanching" and right = "exchanging" + or + wrong = "excisted" and right = "existed" + or + wrong = "exculsivly" and right = "exclusively" + or + wrong = "execising" and right = "exercising" + or + wrong = "exection" and right = "execution" + or + wrong = "exectued" and right = "executed" + or + wrong = "exeedingly" and right = "exceedingly" + or + wrong = "exelent" and right = "excellent" + or + wrong = "exellent" and right = "excellent" + or + wrong = "exemple" and right = "example" + or + wrong = "exept" and right = "except" + or + wrong = "exeptional" and right = "exceptional" + or + wrong = "exerbate" and right = "exacerbate" + or + wrong = "exerbated" and right = "exacerbated" + or + wrong = "exerciese" and right = "exercises" + or + wrong = "exerpt" and right = "excerpt" + or + wrong = "exerpts" and right = "excerpts" + or + wrong = "exersize" and right = "exercise" + or + wrong = "exerternal" and right = "external" + or + wrong = "exhalted" and right = "exalted" + or + wrong = "exhibtion" and right = "exhibition" + or + wrong = "exibition" and right = "exhibition" + or + wrong = "exibitions" and right = "exhibitions" + or + wrong = "exicting" and right = "exciting" + or + wrong = "exinct" and right = "extinct" + or + wrong = "existance" and right = "existence" + or + wrong = "existant" and right = "existent" + or + wrong = "existince" and right = "existence" + or + wrong = "exliled" and right = "exiled" + or + wrong = "exludes" and right = "excludes" + or + wrong = "exmaple" and right = "example" + or + wrong = "exonorate" and right = "exonerate" + or + wrong = "exoskelaton" and right = "exoskeleton" + or + wrong = "expalin" and right = "explain" + or + wrong = "expatriot" and right = "expatriate" + or + wrong = "expeced" and right = "expected" + or + wrong = "expecially" and right = "especially" + or + wrong = "expeditonary" and right = "expeditionary" + or + wrong = "expeiments" and right = "experiments" + or + wrong = "expell" and right = "expel" + or + wrong = "expells" and right = "expels" + or + wrong = "experiance" and right = "experience" + or + wrong = "experianced" and right = "experienced" + or + wrong = "expession" and right = "expression" + or + wrong = "expessions" and right = "expressions" + or + wrong = "expiditions" and right = "expeditions" + or + wrong = "expierence" and right = "experience" + or + wrong = "explaination" and right = "explanation" + or + wrong = "explaning" and right = "explaining" + or + wrong = "explictly" and right = "explicitly" + or + wrong = "exploititive" and right = "exploitative" + or + wrong = "explotation" and right = "exploitation" + or + wrong = "expropiated" and right = "expropriated" + or + wrong = "expropiation" and right = "expropriation" + or + wrong = "exressed" and right = "expressed" + or + wrong = "extemely" and right = "extremely" + or + wrong = "extened" and right = "extended" + or + wrong = "extention" and right = "extension" + or + wrong = "extentions" and right = "extensions" + or + wrong = "extered" and right = "exerted" + or + wrong = "extermist" and right = "extremist" + or + wrong = "extint" and right = "extant" + or + wrong = "extint" and right = "extinct" + or + wrong = "extracter" and right = "extractor" + or + wrong = "extradiction" and right = "extradition" + or + wrong = "extraterrestial" and right = "extraterrestrial" + or + wrong = "extraterrestials" and right = "extraterrestrials" + or + wrong = "extravagent" and right = "extravagant" + or + wrong = "extrememly" and right = "extremely" + or + wrong = "extremeophile" and right = "extremophile" + or + wrong = "extremly" and right = "extremely" + or + wrong = "extrordinarily" and right = "extraordinarily" + or + wrong = "extrordinary" and right = "extraordinary" + or + wrong = "eyar" and right = "eyas" + or + wrong = "eyar" and right = "year" + or + wrong = "eyars" and right = "eyas" + or + wrong = "eyars" and right = "years" + or + wrong = "eyasr" and right = "eyas" + or + wrong = "eyasr" and right = "years" + or + wrong = "faciliate" and right = "facilitate" + or + wrong = "faciliated" and right = "facilitated" + or + wrong = "faciliates" and right = "facilitates" + or + wrong = "facilites" and right = "facilities" + or + wrong = "facillitate" and right = "facilitate" + or + wrong = "facinated" and right = "fascinated" + or + wrong = "facist" and right = "fascist" + or + wrong = "familes" and right = "families" + or + wrong = "familliar" and right = "familiar" + or + wrong = "famoust" and right = "famous" + or + wrong = "fanatism" and right = "fanaticism" + or + wrong = "farenheit" and right = "fahrenheit" + or + wrong = "fatc" and right = "fact" + or + wrong = "faught" and right = "fought" + or + wrong = "favoutrable" and right = "favourable" + or + wrong = "feasable" and right = "feasible" + or + wrong = "febuary" and right = "february" + or + wrong = "feburary" and right = "february" + or + wrong = "fedreally" and right = "federally" + or + wrong = "femminist" and right = "feminist" + or + wrong = "feromone" and right = "pheromone" + or + wrong = "fertily" and right = "fertility" + or + wrong = "fianite" and right = "finite" + or + wrong = "fianlly" and right = "finally" + or + wrong = "ficticious" and right = "fictitious" + or + wrong = "fictious" and right = "fictitious" + or + wrong = "fidn" and right = "find" + or + wrong = "fiel" and right = "feel" + or + wrong = "fiel" and right = "field" + or + wrong = "fiel" and right = "file" + or + wrong = "fiel" and right = "phial" + or + wrong = "fiels" and right = "feels" + or + wrong = "fiels" and right = "fields" + or + wrong = "fiels" and right = "files" + or + wrong = "fiels" and right = "phials" + or + wrong = "fiercly" and right = "fiercely" + or + wrong = "fightings" and right = "fighting" + or + wrong = "filiament" and right = "filament" + or + wrong = "fimilies" and right = "families" + or + wrong = "finacial" and right = "financial" + or + wrong = "finaly" and right = "finally" + or + wrong = "financialy" and right = "financially" + or + wrong = "firends" and right = "friends" + or + wrong = "firts" and right = "first" + or + wrong = "firts" and right = "flirts" + or + wrong = "fisionable" and right = "fissionable" + or + wrong = "flamable" and right = "flammable" + or + wrong = "flawess" and right = "flawless" + or + wrong = "fleed" and right = "fled" + or + wrong = "fleed" and right = "freed" + or + wrong = "flemmish" and right = "flemish" + or + wrong = "florescent" and right = "fluorescent" + or + wrong = "flourescent" and right = "fluorescent" + or + wrong = "flourine" and right = "fluorine" + or + wrong = "flourishment" and right = "flourishing" + or + wrong = "fluorish" and right = "flourish" + or + wrong = "follwoing" and right = "following" + or + wrong = "folowing" and right = "following" + or + wrong = "fomed" and right = "formed" + or + wrong = "fomr" and right = "form" + or + wrong = "fomr" and right = "from" + or + wrong = "fonetic" and right = "phonetic" + or + wrong = "fontrier" and right = "fontier" + or + wrong = "foootball" and right = "football" + or + wrong = "forbad" and right = "forbade" + or + wrong = "forbiden" and right = "forbidden" + or + wrong = "foreward" and right = "foreword" + or + wrong = "forfiet" and right = "forfeit" + or + wrong = "forhead" and right = "forehead" + or + wrong = "foriegn" and right = "foreign" + or + wrong = "formalhaut" and right = "fomalhaut" + or + wrong = "formallize" and right = "formalize" + or + wrong = "formallized" and right = "formalized" + or + wrong = "formaly" and right = "formally" + or + wrong = "formaly" and right = "formerly" + or + wrong = "formelly" and right = "formerly" + or + wrong = "formidible" and right = "formidable" + or + wrong = "formost" and right = "foremost" + or + wrong = "forsaw" and right = "foresaw" + or + wrong = "forseeable" and right = "foreseeable" + or + wrong = "fortelling" and right = "foretelling" + or + wrong = "forunner" and right = "forerunner" + or + wrong = "foucs" and right = "focus" + or + wrong = "foudn" and right = "found" + or + wrong = "fougth" and right = "fought" + or + wrong = "foundaries" and right = "foundries" + or + wrong = "foundary" and right = "foundry" + or + wrong = "foundland" and right = "newfoundland" + or + wrong = "fourties" and right = "forties" + or + wrong = "fourty" and right = "forty" + or + wrong = "fouth" and right = "fourth" + or + wrong = "foward" and right = "forward" + or + wrong = "framwork" and right = "framework" + or + wrong = "fransiscan" and right = "franciscan" + or + wrong = "fransiscans" and right = "franciscans" + or + wrong = "freind" and right = "friend" + or + wrong = "freindly" and right = "friendly" + or + wrong = "frequentily" and right = "frequently" + or + wrong = "frome" and right = "from" + or + wrong = "fromed" and right = "formed" + or + wrong = "froniter" and right = "frontier" + or + wrong = "fucntion" and right = "function" + or + wrong = "fucntioning" and right = "functioning" + or + wrong = "fufill" and right = "fulfill" + or + wrong = "fufilled" and right = "fulfilled" + or + wrong = "fulfiled" and right = "fulfilled" + or + wrong = "fullfill" and right = "fulfill" + or + wrong = "fullfilled" and right = "fulfilled" + or + wrong = "funcion" and right = "function" + or + wrong = "fundametal" and right = "fundamental" + or + wrong = "fundametals" and right = "fundamentals" + or + wrong = "funguses" and right = "fungi" + or + wrong = "funtion" and right = "function" + or + wrong = "funtions" and right = "functions" + or + wrong = "furuther" and right = "further" + or + wrong = "futher" and right = "further" + or + wrong = "futhermore" and right = "furthermore" + or + wrong = "futhroc" and right = "futhark" + or + wrong = "futhroc" and right = "futhorc" + or + wrong = "gae" and right = "gael" + or + wrong = "gae" and right = "gale" + or + wrong = "gae" and right = "game" + or + wrong = "galatic" and right = "galactic" + or + wrong = "galations" and right = "galatians" + or + wrong = "gallaxies" and right = "galaxies" + or + wrong = "galvinized" and right = "galvanized" + or + wrong = "ganerate" and right = "generate" + or + wrong = "ganes" and right = "games" + or + wrong = "ganster" and right = "gangster" + or + wrong = "garantee" and right = "guarantee" + or + wrong = "garanteed" and right = "guaranteed" + or + wrong = "garantees" and right = "guarantees" + or + wrong = "garnison" and right = "garrison" + or + wrong = "gaurantee" and right = "guarantee" + or + wrong = "gauranteed" and right = "guaranteed" + or + wrong = "gaurantees" and right = "guarantees" + or + wrong = "gaurd" and right = "gourd" + or + wrong = "gaurd" and right = "guard" + or + wrong = "gaurentee" and right = "guarantee" + or + wrong = "gaurenteed" and right = "guaranteed" + or + wrong = "gaurentees" and right = "guarantees" + or + wrong = "geneological" and right = "genealogical" + or + wrong = "geneologies" and right = "genealogies" + or + wrong = "geneology" and right = "genealogy" + or + wrong = "generaly" and right = "generally" + or + wrong = "generatting" and right = "generating" + or + wrong = "genialia" and right = "genitalia" + or + wrong = "geographicial" and right = "geographical" + or + wrong = "geometrician" and right = "geometer" + or + wrong = "geometricians" and right = "geometers" + or + wrong = "gerat" and right = "great" + or + wrong = "ghandi" and right = "gandhi" + or + wrong = "glamourous" and right = "glamorous" + or + wrong = "glight" and right = "flight" + or + wrong = "gnawwed" and right = "gnawed" + or + wrong = "godess" and right = "goddess" + or + wrong = "godesses" and right = "goddesses" + or + wrong = "godounov" and right = "godunov" + or + wrong = "gogin" and right = "gauguin" + or + wrong = "gogin" and right = "going" + or + wrong = "goign" and right = "going" + or + wrong = "gonig" and right = "going" + or + wrong = "gothenberg" and right = "gothenburg" + or + wrong = "gottleib" and right = "gottlieb" + or + wrong = "gouvener" and right = "governor" + or + wrong = "govement" and right = "government" + or + wrong = "govenment" and right = "government" + or + wrong = "govenrment" and right = "government" + or + wrong = "goverance" and right = "governance" + or + wrong = "goverment" and right = "government" + or + wrong = "govermental" and right = "governmental" + or + wrong = "governer" and right = "governor" + or + wrong = "governmnet" and right = "government" + or + wrong = "govorment" and right = "government" + or + wrong = "govormental" and right = "governmental" + or + wrong = "govornment" and right = "government" + or + wrong = "gracefull" and right = "graceful" + or + wrong = "graet" and right = "great" + or + wrong = "grafitti" and right = "graffiti" + or + wrong = "gramatically" and right = "grammatically" + or + wrong = "grammaticaly" and right = "grammatically" + or + wrong = "grammer" and right = "grammar" + or + wrong = "grat" and right = "great" + or + wrong = "gratuitious" and right = "gratuitous" + or + wrong = "greatful" and right = "grateful" + or + wrong = "greatfully" and right = "gratefully" + or + wrong = "greif" and right = "grief" + or + wrong = "gridles" and right = "griddles" + or + wrong = "gropu" and right = "group" + or + wrong = "grwo" and right = "grow" + or + wrong = "guaduloupe" and right = "guadalupe" + or + wrong = "guaduloupe" and right = "guadeloupe" + or + wrong = "guadulupe" and right = "guadalupe" + or + wrong = "guadulupe" and right = "guadeloupe" + or + wrong = "guage" and right = "gauge" + or + wrong = "guarentee" and right = "guarantee" + or + wrong = "guarenteed" and right = "guaranteed" + or + wrong = "guarentees" and right = "guarantees" + or + wrong = "guatamala" and right = "guatemala" + or + wrong = "guatamalan" and right = "guatemalan" + or + wrong = "guerrila" and right = "guerrilla" + or + wrong = "guerrilas" and right = "guerrillas" + or + wrong = "guidence" and right = "guidance" + or + wrong = "guilia" and right = "giulia" + or + wrong = "guilio" and right = "giulio" + or + wrong = "guiness" and right = "guinness" + or + wrong = "guiseppe" and right = "giuseppe" + or + wrong = "gunanine" and right = "guanine" + or + wrong = "gurantee" and right = "guarantee" + or + wrong = "guranteed" and right = "guaranteed" + or + wrong = "gurantees" and right = "guarantees" + or + wrong = "guttaral" and right = "guttural" + or + wrong = "gutteral" and right = "guttural" + or + wrong = "habaeus" and right = "habeas" + or + wrong = "habeus" and right = "habeas" + or + wrong = "habsbourg" and right = "habsburg" + or + wrong = "haemorrage" and right = "haemorrhage" + or + wrong = "haev" and right = "have" + or + wrong = "haev" and right = "heave" + or + wrong = "halarious" and right = "hilarious" + or + wrong = "hallowean" and right = "halloween" + or + wrong = "halp" and right = "help" + or + wrong = "hander" and right = "handler" + or + wrong = "hapen" and right = "happen" + or + wrong = "hapened" and right = "happened" + or + wrong = "hapening" and right = "happening" + or + wrong = "happend" and right = "happened" + or + wrong = "happended" and right = "happened" + or + wrong = "happenned" and right = "happened" + or + wrong = "harased" and right = "harassed" + or + wrong = "harases" and right = "harasses" + or + wrong = "harasment" and right = "harassment" + or + wrong = "harasments" and right = "harassments" + or + wrong = "harassement" and right = "harassment" + or + wrong = "harras" and right = "harass" + or + wrong = "harrased" and right = "harassed" + or + wrong = "harrases" and right = "harasses" + or + wrong = "harrasing" and right = "harassing" + or + wrong = "harrasment" and right = "harassment" + or + wrong = "harrasments" and right = "harassments" + or + wrong = "harrassed" and right = "harassed" + or + wrong = "harrasses" and right = "harassed" + or + wrong = "harrassing" and right = "harassing" + or + wrong = "harrassment" and right = "harassment" + or + wrong = "harrassments" and right = "harassments" + or + wrong = "hatian" and right = "haitian" + or + wrong = "haviest" and right = "heaviest" + or + wrong = "headquarer" and right = "headquarter" + or + wrong = "headquater" and right = "headquarter" + or + wrong = "headquatered" and right = "headquartered" + or + wrong = "headquaters" and right = "headquarters" + or + wrong = "healthercare" and right = "healthcare" + or + wrong = "heared" and right = "heard" + or + wrong = "heathy" and right = "healthy" + or + wrong = "heidelburg" and right = "heidelberg" + or + wrong = "heigher" and right = "higher" + or + wrong = "heirarchy" and right = "hierarchy" + or + wrong = "heiroglyphics" and right = "hieroglyphics" + or + wrong = "helment" and right = "helmet" + or + wrong = "helpfull" and right = "helpful" + or + wrong = "helpped" and right = "helped" + or + wrong = "hemmorhage" and right = "hemorrhage" + or + wrong = "herad" and right = "heard" + or + wrong = "herad" and right = "hera" + or + wrong = "heridity" and right = "heredity" + or + wrong = "heroe" and right = "hero" + or + wrong = "heros" and right = "heroes" + or + wrong = "hertiage" and right = "heritage" + or + wrong = "hertzs" and right = "hertz" + or + wrong = "hesistant" and right = "hesitant" + or + wrong = "heterogenous" and right = "heterogeneous" + or + wrong = "hieght" and right = "height" + or + wrong = "hierachical" and right = "hierarchical" + or + wrong = "hierachies" and right = "hierarchies" + or + wrong = "hierachy" and right = "hierarchy" + or + wrong = "hierarcical" and right = "hierarchical" + or + wrong = "hierarcy" and right = "hierarchy" + or + wrong = "hieroglph" and right = "hieroglyph" + or + wrong = "hieroglphs" and right = "hieroglyphs" + or + wrong = "higer" and right = "higher" + or + wrong = "higest" and right = "highest" + or + wrong = "higway" and right = "highway" + or + wrong = "hillarious" and right = "hilarious" + or + wrong = "himselv" and right = "himself" + or + wrong = "hinderance" and right = "hindrance" + or + wrong = "hinderence" and right = "hindrance" + or + wrong = "hindrence" and right = "hindrance" + or + wrong = "hipopotamus" and right = "hippopotamus" + or + wrong = "hismelf" and right = "himself" + or + wrong = "histocompatability" and right = "histocompatibility" + or + wrong = "historicians" and right = "historians" + or + wrong = "holf" and right = "hold" + or + wrong = "holliday" and right = "holiday" + or + wrong = "homogeneize" and right = "homogenize" + or + wrong = "homogeneized" and right = "homogenized" + or + wrong = "honory" and right = "honorary" + or + wrong = "horrifing" and right = "horrifying" + or + wrong = "hosited" and right = "hoisted" + or + wrong = "hospitible" and right = "hospitable" + or + wrong = "hounour" and right = "honour" + or + wrong = "housr" and right = "hours" + or + wrong = "housr" and right = "house" + or + wrong = "howver" and right = "however" + or + wrong = "hsitorians" and right = "historians" + or + wrong = "hstory" and right = "history" + or + wrong = "hten" and right = "hen" + or + wrong = "hten" and right = "the" + or + wrong = "hten" and right = "then" + or + wrong = "htere" and right = "here" + or + wrong = "htere" and right = "there" + or + wrong = "htey" and right = "they" + or + wrong = "htikn" and right = "think" + or + wrong = "hting" and right = "thing" + or + wrong = "htink" and right = "think" + or + wrong = "htis" and right = "this" + or + wrong = "humer" and right = "humor" + or + wrong = "humer" and right = "humour" + or + wrong = "humerous" and right = "humerus" + or + wrong = "humerous" and right = "humorous" + or + wrong = "huminoid" and right = "humanoid" + or + wrong = "humoural" and right = "humoral" + or + wrong = "humurous" and right = "humorous" + or + wrong = "husban" and right = "husband" + or + wrong = "hvae" and right = "have" + or + wrong = "hvaing" and right = "having" + or + wrong = "hvea" and right = "have" + or + wrong = "hvea" and right = "heave" + or + wrong = "hwihc" and right = "which" + or + wrong = "hwile" and right = "while" + or + wrong = "hwole" and right = "whole" + or + wrong = "hydogen" and right = "hydrogen" + or + wrong = "hydropile" and right = "hydrophile" + or + wrong = "hydropilic" and right = "hydrophilic" + or + wrong = "hydropobe" and right = "hydrophobe" + or + wrong = "hydropobic" and right = "hydrophobic" + or + wrong = "hygeine" and right = "hygiene" + or + wrong = "hyjack" and right = "hijack" + or + wrong = "hyjacking" and right = "hijacking" + or + wrong = "hypocracy" and right = "hypocrisy" + or + wrong = "hypocrasy" and right = "hypocrisy" + or + wrong = "hypocricy" and right = "hypocrisy" + or + wrong = "hypocrit" and right = "hypocrite" + or + wrong = "hypocrits" and right = "hypocrites" + or + wrong = "iconclastic" and right = "iconoclastic" + or + wrong = "idaeidae" and right = "idea" + or + wrong = "idaes" and right = "ideas" + or + wrong = "idealogies" and right = "ideologies" + or + wrong = "idealogy" and right = "ideology" + or + wrong = "identicial" and right = "identical" + or + wrong = "identifers" and right = "identifiers" + or + wrong = "ideosyncratic" and right = "idiosyncratic" + or + wrong = "idesa" and right = "ideas" + or + wrong = "idesa" and right = "ides" + or + wrong = "idiosyncracy" and right = "idiosyncrasy" + or + wrong = "ihaca" and right = "ithaca" + or + wrong = "illegimacy" and right = "illegitimacy" + or + wrong = "illegitmate" and right = "illegitimate" + or + wrong = "illess" and right = "illness" + or + wrong = "illiegal" and right = "illegal" + or + wrong = "illution" and right = "illusion" + or + wrong = "ilness" and right = "illness" + or + wrong = "ilogical" and right = "illogical" + or + wrong = "imagenary" and right = "imaginary" + or + wrong = "imagin" and right = "imagine" + or + wrong = "imaginery" and right = "imagery" + or + wrong = "imaginery" and right = "imaginary" + or + wrong = "imanent" and right = "eminent" + or + wrong = "imanent" and right = "imminent" + or + wrong = "imcomplete" and right = "incomplete" + or + wrong = "imediately" and right = "immediately" + or + wrong = "imense" and right = "immense" + or + wrong = "imigrant" and right = "emigrant" + or + wrong = "imigrant" and right = "immigrant" + or + wrong = "imigrated" and right = "emigrated" + or + wrong = "imigrated" and right = "immigrated" + or + wrong = "imigration" and right = "emigration" + or + wrong = "imigration" and right = "immigration" + or + wrong = "iminent" and right = "eminent" + or + wrong = "iminent" and right = "immanent" + or + wrong = "iminent" and right = "imminent" + or + wrong = "immediatley" and right = "immediately" + or + wrong = "immediatly" and right = "immediately" + or + wrong = "immidately" and right = "immediately" + or + wrong = "immidiately" and right = "immediately" + or + wrong = "immitate" and right = "imitate" + or + wrong = "immitated" and right = "imitated" + or + wrong = "immitating" and right = "imitating" + or + wrong = "immitator" and right = "imitator" + or + wrong = "immunosupressant" and right = "immunosuppressant" + or + wrong = "impecabbly" and right = "impeccably" + or + wrong = "impedence" and right = "impedance" + or + wrong = "implamenting" and right = "implementing" + or + wrong = "impliment" and right = "implement" + or + wrong = "implimented" and right = "implemented" + or + wrong = "imploys" and right = "employs" + or + wrong = "importamt" and right = "important" + or + wrong = "impressario" and right = "impresario" + or + wrong = "imprioned" and right = "imprisoned" + or + wrong = "imprisonned" and right = "imprisoned" + or + wrong = "improvision" and right = "improvisation" + or + wrong = "improvments" and right = "improvements" + or + wrong = "inablility" and right = "inability" + or + wrong = "inaccessable" and right = "inaccessible" + or + wrong = "inadiquate" and right = "inadequate" + or + wrong = "inadquate" and right = "inadequate" + or + wrong = "inadvertant" and right = "inadvertent" + or + wrong = "inadvertantly" and right = "inadvertently" + or + wrong = "inagurated" and right = "inaugurated" + or + wrong = "inaguration" and right = "inauguration" + or + wrong = "inappropiate" and right = "inappropriate" + or + wrong = "inaugures" and right = "inaugurates" + or + wrong = "inbalance" and right = "imbalance" + or + wrong = "inbalanced" and right = "imbalanced" + or + wrong = "inbetween" and right = "between" + or + wrong = "incarcirated" and right = "incarcerated" + or + wrong = "incidentially" and right = "incidentally" + or + wrong = "incidently" and right = "incidentally" + or + wrong = "inclreased" and right = "increased" + or + wrong = "includ" and right = "include" + or + wrong = "includng" and right = "including" + or + wrong = "incompatabilities" and right = "incompatibilities" + or + wrong = "incompatability" and right = "incompatibility" + or + wrong = "incompatable" and right = "incompatible" + or + wrong = "incompatablities" and right = "incompatibilities" + or + wrong = "incompatablity" and right = "incompatibility" + or + wrong = "incompatiblities" and right = "incompatibilities" + or + wrong = "incompatiblity" and right = "incompatibility" + or + wrong = "incompetance" and right = "incompetence" + or + wrong = "incompetant" and right = "incompetent" + or + wrong = "incomptable" and right = "incompatible" + or + wrong = "incomptetent" and right = "incompetent" + or + wrong = "inconsistant" and right = "inconsistent" + or + wrong = "incoroporated" and right = "incorporated" + or + wrong = "incorperation" and right = "incorporation" + or + wrong = "incorportaed" and right = "incorporated" + or + wrong = "incorprates" and right = "incorporates" + or + wrong = "incorruptable" and right = "incorruptible" + or + wrong = "incramentally" and right = "incrementally" + or + wrong = "increadible" and right = "incredible" + or + wrong = "incredable" and right = "incredible" + or + wrong = "inctroduce" and right = "introduce" + or + wrong = "inctroduced" and right = "introduced" + or + wrong = "incuding" and right = "including" + or + wrong = "incunabla" and right = "incunabula" + or + wrong = "indefinately" and right = "indefinitely" + or + wrong = "indefineable" and right = "undefinable" + or + wrong = "indefinitly" and right = "indefinitely" + or + wrong = "indentical" and right = "identical" + or + wrong = "indepedantly" and right = "independently" + or + wrong = "indepedence" and right = "independence" + or + wrong = "independance" and right = "independence" + or + wrong = "independant" and right = "independent" + or + wrong = "independantly" and right = "independently" + or + wrong = "independece" and right = "independence" + or + wrong = "independendet" and right = "independent" + or + wrong = "indespensable" and right = "indispensable" + or + wrong = "indespensible" and right = "indispensable" + or + wrong = "indictement" and right = "indictment" + or + wrong = "indigineous" and right = "indigenous" + or + wrong = "indipendence" and right = "independence" + or + wrong = "indipendent" and right = "independent" + or + wrong = "indipendently" and right = "independently" + or + wrong = "indispensible" and right = "indispensable" + or + wrong = "indisputible" and right = "indisputable" + or + wrong = "indisputibly" and right = "indisputably" + or + wrong = "indite" and right = "indict" + or + wrong = "individualy" and right = "individually" + or + wrong = "indpendent" and right = "independent" + or + wrong = "indpendently" and right = "independently" + or + wrong = "indulgue" and right = "indulge" + or + wrong = "indutrial" and right = "industrial" + or + wrong = "indviduals" and right = "individuals" + or + wrong = "inefficienty" and right = "inefficiently" + or + wrong = "inevatible" and right = "inevitable" + or + wrong = "inevitible" and right = "inevitable" + or + wrong = "inevititably" and right = "inevitably" + or + wrong = "infalability" and right = "infallibility" + or + wrong = "infallable" and right = "infallible" + or + wrong = "infectuous" and right = "infectious" + or + wrong = "infered" and right = "inferred" + or + wrong = "infilitrate" and right = "infiltrate" + or + wrong = "infilitrated" and right = "infiltrated" + or + wrong = "infilitration" and right = "infiltration" + or + wrong = "infinit" and right = "infinite" + or + wrong = "inflamation" and right = "inflammation" + or + wrong = "influencial" and right = "influential" + or + wrong = "influented" and right = "influenced" + or + wrong = "infomation" and right = "information" + or + wrong = "informtion" and right = "information" + or + wrong = "infrantryman" and right = "infantryman" + or + wrong = "infrigement" and right = "infringement" + or + wrong = "ingenius" and right = "ingenious" + or + wrong = "ingreediants" and right = "ingredients" + or + wrong = "inhabitans" and right = "inhabitants" + or + wrong = "inherantly" and right = "inherently" + or + wrong = "inheritage" and right = "heritage" + or + wrong = "inheritage" and right = "inheritance" + or + wrong = "inheritence" and right = "inheritance" + or + wrong = "inital" and right = "initial" + or + wrong = "initalize" and right = "initialize" + or + wrong = "initally" and right = "initially" + or + wrong = "initation" and right = "initiation" + or + wrong = "initiaitive" and right = "initiative" + or + wrong = "inlcuding" and right = "including" + or + wrong = "inmigrant" and right = "immigrant" + or + wrong = "inmigrants" and right = "immigrants" + or + wrong = "innoculated" and right = "inoculated" + or + wrong = "inocence" and right = "innocence" + or + wrong = "inofficial" and right = "unofficial" + or + wrong = "inot" and right = "into" + or + wrong = "inpeach" and right = "impeach" + or + wrong = "inpending" and right = "impending" + or + wrong = "inpenetrable" and right = "impenetrable" + or + wrong = "inpolite" and right = "impolite" + or + wrong = "inprisonment" and right = "imprisonment" + or + wrong = "inproving" and right = "improving" + or + wrong = "insectiverous" and right = "insectivorous" + or + wrong = "insensative" and right = "insensitive" + or + wrong = "inseperable" and right = "inseparable" + or + wrong = "insistance" and right = "insistence" + or + wrong = "insitution" and right = "institution" + or + wrong = "insitutions" and right = "institutions" + or + wrong = "inspite" and right = "inspire" + or + wrong = "instade" and right = "instead" + or + wrong = "instatance" and right = "instance" + or + wrong = "institue" and right = "institute" + or + wrong = "instuction" and right = "instruction" + or + wrong = "instuments" and right = "instruments" + or + wrong = "instutionalized" and right = "institutionalized" + or + wrong = "instutions" and right = "intuitions" + or + wrong = "insurence" and right = "insurance" + or + wrong = "intelectual" and right = "intellectual" + or + wrong = "inteligence" and right = "intelligence" + or + wrong = "inteligent" and right = "intelligent" + or + wrong = "intenational" and right = "international" + or + wrong = "intented" and right = "indented" + or + wrong = "intented" and right = "intended" + or + wrong = "intepretation" and right = "interpretation" + or + wrong = "intepretator" and right = "interpretor" + or + wrong = "interational" and right = "international" + or + wrong = "interbread" and right = "interbred" + or + wrong = "interbread" and right = "interbreed" + or + wrong = "interchangable" and right = "interchangeable" + or + wrong = "interchangably" and right = "interchangeably" + or + wrong = "intercontinential" and right = "intercontinental" + or + wrong = "intercontinetal" and right = "intercontinental" + or + wrong = "intered" and right = "interned" + or + wrong = "intered" and right = "interred" + or + wrong = "interelated" and right = "interrelated" + or + wrong = "interferance" and right = "interference" + or + wrong = "interfereing" and right = "interfering" + or + wrong = "intergrated" and right = "integrated" + or + wrong = "intergration" and right = "integration" + or + wrong = "interm" and right = "interim" + or + wrong = "internation" and right = "international" + or + wrong = "interpet" and right = "interpret" + or + wrong = "interrim" and right = "interim" + or + wrong = "interrugum" and right = "interregnum" + or + wrong = "intertaining" and right = "entertaining" + or + wrong = "interupt" and right = "interrupt" + or + wrong = "intervines" and right = "intervenes" + or + wrong = "intevene" and right = "intervene" + or + wrong = "intial" and right = "initial" + or + wrong = "intialize" and right = "initialize" + or + wrong = "intialized" and right = "initialized" + or + wrong = "intially" and right = "initially" + or + wrong = "intrduced" and right = "introduced" + or + wrong = "intrest" and right = "interest" + or + wrong = "introdued" and right = "introduced" + or + wrong = "intruduced" and right = "introduced" + or + wrong = "intrument" and right = "instrument" + or + wrong = "intrumental" and right = "instrumental" + or + wrong = "intruments" and right = "instruments" + or + wrong = "intrusted" and right = "entrusted" + or + wrong = "intutive" and right = "intuitive" + or + wrong = "intutively" and right = "intuitively" + or + wrong = "inudstry" and right = "industry" + or + wrong = "inumerable" and right = "enumerable" + or + wrong = "inumerable" and right = "innumerable" + or + wrong = "inventer" and right = "inventor" + or + wrong = "invertibrates" and right = "invertebrates" + or + wrong = "investingate" and right = "investigate" + or + wrong = "involvment" and right = "involvement" + or + wrong = "irelevent" and right = "irrelevant" + or + wrong = "iresistable" and right = "irresistible" + or + wrong = "iresistably" and right = "irresistibly" + or + wrong = "iresistible" and right = "irresistible" + or + wrong = "iresistibly" and right = "irresistibly" + or + wrong = "iritable" and right = "irritable" + or + wrong = "iritated" and right = "irritated" + or + wrong = "ironicly" and right = "ironically" + or + wrong = "irregardless" and right = "regardless" + or + wrong = "irrelevent" and right = "irrelevant" + or + wrong = "irreplacable" and right = "irreplaceable" + or + wrong = "irresistable" and right = "irresistible" + or + wrong = "irresistably" and right = "irresistibly" + or + wrong = "israelies" and right = "israelis" + or + wrong = "issueing" and right = "issuing" + or + wrong = "itnroduced" and right = "introduced" + or + wrong = "iunior" and right = "junior" + or + wrong = "iwll" and right = "will" + or + wrong = "iwth" and right = "with" + or + wrong = "janurary" and right = "january" + or + wrong = "januray" and right = "january" + or + wrong = "japanes" and right = "japanese" + or + wrong = "jaques" and right = "jacques" + or + wrong = "jeapardy" and right = "jeopardy" + or + wrong = "jewllery" and right = "jewellery" + or + wrong = "johanine" and right = "johannine" + or + wrong = "jorunal" and right = "journal" + or + wrong = "jospeh" and right = "joseph" + or + wrong = "jouney" and right = "journey" + or + wrong = "journied" and right = "journeyed" + or + wrong = "journies" and right = "journeys" + or + wrong = "jstu" and right = "just" + or + wrong = "jsut" and right = "just" + or + wrong = "juadaism" and right = "judaism" + or + wrong = "juadism" and right = "judaism" + or + wrong = "judical" and right = "judicial" + or + wrong = "judisuary" and right = "judiciary" + or + wrong = "juducial" and right = "judicial" + or + wrong = "juristiction" and right = "jurisdiction" + or + wrong = "juristictions" and right = "jurisdictions" + or + wrong = "kindergarden" and right = "kindergarten" + or + wrong = "klenex" and right = "kleenex" + or + wrong = "knifes" and right = "knives" + or + wrong = "knive" and right = "knife" + or + wrong = "knowlege" and right = "knowledge" + or + wrong = "knowlegeable" and right = "knowledgeable" + or + wrong = "knwo" and right = "know" + or + wrong = "knwos" and right = "knows" + or + wrong = "konw" and right = "know" + or + wrong = "konws" and right = "knows" + or + wrong = "kwno" and right = "know" + or + wrong = "labatory" and right = "laboratory" + or + wrong = "labatory" and right = "lavatory" + or + wrong = "labled" and right = "labeled" + or + wrong = "labled" and right = "labelled" + or + wrong = "labratory" and right = "laboratory" + or + wrong = "laguage" and right = "language" + or + wrong = "laguages" and right = "languages" + or + wrong = "langage" and right = "language" + or + wrong = "langauge" and right = "language" + or + wrong = "larg" and right = "large" + or + wrong = "largst" and right = "largest" + or + wrong = "larrry" and right = "larry" + or + wrong = "lastr" and right = "last" + or + wrong = "lattitude" and right = "latitude" + or + wrong = "launchs" and right = "launch" + or + wrong = "launchs" and right = "launches" + or + wrong = "launhed" and right = "launched" + or + wrong = "lavae" and right = "larvae" + or + wrong = "layed" and right = "laid" + or + wrong = "lazyness" and right = "laziness" + or + wrong = "leage" and right = "league" + or + wrong = "leanr" and right = "lean" + or + wrong = "leanr" and right = "leaner" + or + wrong = "leanr" and right = "learn" + or + wrong = "leathal" and right = "lethal" + or + wrong = "lefted" and right = "left" + or + wrong = "legitamate" and right = "legitimate" + or + wrong = "legitmate" and right = "legitimate" + or + wrong = "leibnitz" and right = "leibniz" + or + wrong = "lengh" and right = "length" + or + wrong = "lenght" and right = "length" + or + wrong = "lengt" and right = "length" + or + wrong = "lenth" and right = "length" + or + wrong = "leran" and right = "learn" + or + wrong = "lerans" and right = "learns" + or + wrong = "leutenant" and right = "lieutenant" + or + wrong = "levetate" and right = "levitate" + or + wrong = "levetated" and right = "levitated" + or + wrong = "levetates" and right = "levitates" + or + wrong = "levetating" and right = "levitating" + or + wrong = "levle" and right = "level" + or + wrong = "liasion" and right = "liaison" + or + wrong = "liason" and right = "liaison" + or + wrong = "liasons" and right = "liaisons" + or + wrong = "libaries" and right = "libraries" + or + wrong = "libary" and right = "library" + or + wrong = "libell" and right = "libel" + or + wrong = "libguistic" and right = "linguistic" + or + wrong = "libguistics" and right = "linguistics" + or + wrong = "libitarianisn" and right = "libertarianism" + or + wrong = "lible" and right = "liable" + or + wrong = "lible" and right = "libel" + or + wrong = "lieing" and right = "lying" + or + wrong = "liek" and right = "like" + or + wrong = "liekd" and right = "liked" + or + wrong = "liesure" and right = "leisure" + or + wrong = "lieuenant" and right = "lieutenant" + or + wrong = "lieved" and right = "lived" + or + wrong = "liftime" and right = "lifetime" + or + wrong = "likelyhood" and right = "likelihood" + or + wrong = "linnaena" and right = "linnaean" + or + wrong = "lippizaner" and right = "lipizzaner" + or + wrong = "liquify" and right = "liquefy" + or + wrong = "liscense" and right = "licence" + or + wrong = "liscense" and right = "license" + or + wrong = "lisence" and right = "licence" + or + wrong = "lisence" and right = "license" + or + wrong = "lisense" and right = "licence" + or + wrong = "lisense" and right = "license" + or + wrong = "listners" and right = "listeners" + or + wrong = "litature" and right = "literature" + or + wrong = "literaly" and right = "literally" + or + wrong = "literture" and right = "literature" + or + wrong = "littel" and right = "little" + or + wrong = "litterally" and right = "literally" + or + wrong = "liuke" and right = "like" + or + wrong = "livley" and right = "lively" + or + wrong = "lmits" and right = "limits" + or + wrong = "loev" and right = "love" + or + wrong = "lonelyness" and right = "loneliness" + or + wrong = "longitudonal" and right = "longitudinal" + or + wrong = "lonley" and right = "lonely" + or + wrong = "lonly" and right = "lonely" + or + wrong = "lonly" and right = "only" + or + wrong = "loosing" and right = "losing" + or + wrong = "lotharingen" and right = "lothringen" + or + wrong = "lsat" and right = "last" + or + wrong = "lukid" and right = "likud" + or + wrong = "lveo" and right = "love" + or + wrong = "lvoe" and right = "love" + or + wrong = "lybia" and right = "libya" + or + wrong = "maching" and right = "machine" + or + wrong = "maching" and right = "marching" + or + wrong = "maching" and right = "matching" + or + wrong = "mackeral" and right = "mackerel" + or + wrong = "magasine" and right = "magazine" + or + wrong = "magincian" and right = "magician" + or + wrong = "magisine" and right = "magazine" + or + wrong = "magizine" and right = "magazine" + or + wrong = "magnificient" and right = "magnificent" + or + wrong = "magolia" and right = "magnolia" + or + wrong = "mailny" and right = "mainly" + or + wrong = "maintainance" and right = "maintenance" + or + wrong = "maintainence" and right = "maintenance" + or + wrong = "maintance" and right = "maintenance" + or + wrong = "maintenence" and right = "maintenance" + or + wrong = "maintinaing" and right = "maintaining" + or + wrong = "maintioned" and right = "mentioned" + or + wrong = "majoroty" and right = "majority" + or + wrong = "maked" and right = "made" + or + wrong = "maked" and right = "marked" + or + wrong = "makse" and right = "makes" + or + wrong = "malcom" and right = "malcolm" + or + wrong = "maltesian" and right = "maltese" + or + wrong = "mamal" and right = "mammal" + or + wrong = "mamalian" and right = "mammalian" + or + wrong = "managable" and right = "manageable" + or + wrong = "managable" and right = "manageably" + or + wrong = "managment" and right = "management" + or + wrong = "maneouvre" and right = "manoeuvre" + or + wrong = "maneouvred" and right = "manoeuvred" + or + wrong = "maneouvres" and right = "manoeuvres" + or + wrong = "maneouvring" and right = "manoeuvring" + or + wrong = "manisfestations" and right = "manifestations" + or + wrong = "manoeuverability" and right = "maneuverability" + or + wrong = "manouver" and right = "maneuver" + or + wrong = "manouver" and right = "manoeuvre" + or + wrong = "manouverability" and right = "maneuverability" + or + wrong = "manouverability" and right = "manoeuverability" + or + wrong = "manouverability" and right = "manoeuvrability" + or + wrong = "manouverable" and right = "maneuverable" + or + wrong = "manouverable" and right = "manoeuvrable" + or + wrong = "manouvers" and right = "maneuvers" + or + wrong = "manouvers" and right = "manoeuvres" + or + wrong = "mantained" and right = "maintained" + or + wrong = "manuever" and right = "maneuver" + or + wrong = "manuever" and right = "manoeuvre" + or + wrong = "manuevers" and right = "maneuvers" + or + wrong = "manuevers" and right = "manoeuvres" + or + wrong = "manufacturedd" and right = "manufactured" + or + wrong = "manufature" and right = "manufacture" + or + wrong = "manufatured" and right = "manufactured" + or + wrong = "manufaturing" and right = "manufacturing" + or + wrong = "manuver" and right = "maneuver" + or + wrong = "mariage" and right = "marriage" + or + wrong = "marjority" and right = "majority" + or + wrong = "markes" and right = "marks" + or + wrong = "marketting" and right = "marketing" + or + wrong = "marmelade" and right = "marmalade" + or + wrong = "marrage" and right = "marriage" + or + wrong = "marraige" and right = "marriage" + or + wrong = "marrtyred" and right = "martyred" + or + wrong = "marryied" and right = "married" + or + wrong = "massachussets" and right = "massachusetts" + or + wrong = "massachussetts" and right = "massachusetts" + or + wrong = "masterbation" and right = "masturbation" + or + wrong = "mataphysical" and right = "metaphysical" + or + wrong = "materalists" and right = "materialist" + or + wrong = "mathamatics" and right = "mathematics" + or + wrong = "mathematican" and right = "mathematician" + or + wrong = "mathematicas" and right = "mathematics" + or + wrong = "matheticians" and right = "mathematicians" + or + wrong = "mathmatically" and right = "mathematically" + or + wrong = "mathmatician" and right = "mathematician" + or + wrong = "mathmaticians" and right = "mathematicians" + or + wrong = "mccarthyst" and right = "mccarthyist" + or + wrong = "mchanics" and right = "mechanics" + or + wrong = "meaninng" and right = "meaning" + or + wrong = "mear" and right = "mare" + or + wrong = "mear" and right = "mere" + or + wrong = "mear" and right = "wear" + or + wrong = "mechandise" and right = "merchandise" + or + wrong = "medacine" and right = "medicine" + or + wrong = "medeival" and right = "medieval" + or + wrong = "medevial" and right = "medieval" + or + wrong = "mediciney" and right = "mediciny" + or + wrong = "medievel" and right = "medieval" + or + wrong = "mediterainnean" and right = "mediterranean" + or + wrong = "mediteranean" and right = "mediterranean" + or + wrong = "meerkrat" and right = "meerkat" + or + wrong = "melieux" and right = "milieux" + or + wrong = "membranaphone" and right = "membranophone" + or + wrong = "memeber" and right = "member" + or + wrong = "menally" and right = "mentally" + or + wrong = "meranda" and right = "miranda" + or + wrong = "meranda" and right = "veranda" + or + wrong = "mercentile" and right = "mercantile" + or + wrong = "mesage" and right = "message" + or + wrong = "messanger" and right = "messenger" + or + wrong = "messenging" and right = "messaging" + or + wrong = "messsage" and right = "message" + or + wrong = "metalic" and right = "metallic" + or + wrong = "metalurgic" and right = "metallurgic" + or + wrong = "metalurgical" and right = "metallurgical" + or + wrong = "metalurgy" and right = "metallurgy" + or + wrong = "metamorphysis" and right = "metamorphosis" + or + wrong = "metaphoricial" and right = "metaphorical" + or + wrong = "meterologist" and right = "meteorologist" + or + wrong = "meterology" and right = "meteorology" + or + wrong = "methaphor" and right = "metaphor" + or + wrong = "methaphors" and right = "metaphors" + or + wrong = "michagan" and right = "michigan" + or + wrong = "micoscopy" and right = "microscopy" + or + wrong = "midwifes" and right = "midwives" + or + wrong = "mileau" and right = "milieu" + or + wrong = "milennia" and right = "millennia" + or + wrong = "milennium" and right = "millennium" + or + wrong = "mileu" and right = "milieu" + or + wrong = "miliary" and right = "military" + or + wrong = "miligram" and right = "milligram" + or + wrong = "milion" and right = "million" + or + wrong = "miliraty" and right = "military" + or + wrong = "millenia" and right = "millennia" + or + wrong = "millenial" and right = "millennial" + or + wrong = "millenialism" and right = "millennialism" + or + wrong = "millenium" and right = "millennium" + or + wrong = "millepede" and right = "millipede" + or + wrong = "millioniare" and right = "millionaire" + or + wrong = "millitant" and right = "militant" + or + wrong = "millitary" and right = "military" + or + wrong = "millon" and right = "million" + or + wrong = "miltary" and right = "military" + or + wrong = "minature" and right = "miniature" + or + wrong = "minerial" and right = "mineral" + or + wrong = "ministery" and right = "ministry" + or + wrong = "minsitry" and right = "ministry" + or + wrong = "minstries" and right = "ministries" + or + wrong = "minstry" and right = "ministry" + or + wrong = "minumum" and right = "minimum" + or + wrong = "mirrorred" and right = "mirrored" + or + wrong = "miscelaneous" and right = "miscellaneous" + or + wrong = "miscellanious" and right = "miscellaneous" + or + wrong = "miscellanous" and right = "miscellaneous" + or + wrong = "mischeivous" and right = "mischievous" + or + wrong = "mischevious" and right = "mischievous" + or + wrong = "mischievious" and right = "mischievous" + or + wrong = "misdameanor" and right = "misdemeanor" + or + wrong = "misdameanors" and right = "misdemeanors" + or + wrong = "misdemenor" and right = "misdemeanor" + or + wrong = "misdemenors" and right = "misdemeanors" + or + wrong = "misfourtunes" and right = "misfortunes" + or + wrong = "misile" and right = "missile" + or + wrong = "misouri" and right = "missouri" + or + wrong = "mispell" and right = "misspell" + or + wrong = "mispelled" and right = "misspelled" + or + wrong = "mispelling" and right = "misspelling" + or + wrong = "missen" and right = "mizzen" + or + wrong = "missisipi" and right = "mississippi" + or + wrong = "missisippi" and right = "mississippi" + or + wrong = "missle" and right = "missile" + or + wrong = "missonary" and right = "missionary" + or + wrong = "misterious" and right = "mysterious" + or + wrong = "mistery" and right = "mystery" + or + wrong = "misteryous" and right = "mysterious" + or + wrong = "mkae" and right = "make" + or + wrong = "mkaes" and right = "makes" + or + wrong = "mkaing" and right = "making" + or + wrong = "mkea" and right = "make" + or + wrong = "moderm" and right = "modem" + or + wrong = "modle" and right = "model" + or + wrong = "moent" and right = "moment" + or + wrong = "moeny" and right = "money" + or + wrong = "mohammedans" and right = "muslims" + or + wrong = "moil" and right = "mohel" + or + wrong = "moil" and right = "soil" + or + wrong = "moleclues" and right = "molecules" + or + wrong = "momento" and right = "memento" + or + wrong = "monestaries" and right = "monasteries" + or + wrong = "monestary" and right = "monastery" + or + wrong = "monestary" and right = "monetary" + or + wrong = "monickers" and right = "monikers" + or + wrong = "monolite" and right = "monolithic" + or + wrong = "monserrat" and right = "montserrat" + or + wrong = "montains" and right = "mountains" + or + wrong = "montanous" and right = "mountainous" + or + wrong = "montnana" and right = "montana" + or + wrong = "monts" and right = "months" + or + wrong = "montypic" and right = "monotypic" + or + wrong = "moreso" and right = "more" + or + wrong = "morgage" and right = "mortgage" + or + wrong = "morisette" and right = "morissette" + or + wrong = "morrisette" and right = "morissette" + or + wrong = "morroccan" and right = "moroccan" + or + wrong = "morrocco" and right = "morocco" + or + wrong = "morroco" and right = "morocco" + or + wrong = "mortage" and right = "mortgage" + or + wrong = "mosture" and right = "moisture" + or + wrong = "motiviated" and right = "motivated" + or + wrong = "mounth" and right = "month" + or + wrong = "movei" and right = "movie" + or + wrong = "movment" and right = "movement" + or + wrong = "mroe" and right = "more" + or + wrong = "mucuous" and right = "mucous" + or + wrong = "muder" and right = "murder" + or + wrong = "mudering" and right = "murdering" + or + wrong = "muhammadan" and right = "muslim" + or + wrong = "multicultralism" and right = "multiculturalism" + or + wrong = "multipled" and right = "multiplied" + or + wrong = "multiplers" and right = "multipliers" + or + wrong = "munbers" and right = "numbers" + or + wrong = "muncipalities" and right = "municipalities" + or + wrong = "muncipality" and right = "municipality" + or + wrong = "munnicipality" and right = "municipality" + or + wrong = "muscels" and right = "muscles" + or + wrong = "muscels" and right = "mussels" + or + wrong = "muscial" and right = "musical" + or + wrong = "muscician" and right = "musician" + or + wrong = "muscicians" and right = "musicians" + or + wrong = "mutiliated" and right = "mutilated" + or + wrong = "mutiple" and right = "multiple" + or + wrong = "myraid" and right = "myriad" + or + wrong = "mysef" and right = "myself" + or + wrong = "mysogynist" and right = "misogynist" + or + wrong = "mysogyny" and right = "misogyny" + or + wrong = "mysterous" and right = "mysterious" + or + wrong = "mythraic" and right = "mithraic" + or + wrong = "naieve" and right = "naive" + or + wrong = "naploeon" and right = "napoleon" + or + wrong = "napolean" and right = "napoleon" + or + wrong = "napoleonian" and right = "napoleonic" + or + wrong = "naturaly" and right = "naturally" + or + wrong = "naturely" and right = "naturally" + or + wrong = "naturual" and right = "natural" + or + wrong = "naturually" and right = "naturally" + or + wrong = "nazereth" and right = "nazareth" + or + wrong = "neccesarily" and right = "necessarily" + or + wrong = "neccesary" and right = "necessary" + or + wrong = "neccessarily" and right = "necessarily" + or + wrong = "neccessary" and right = "necessary" + or + wrong = "neccessities" and right = "necessities" + or + wrong = "necesarily" and right = "necessarily" + or + wrong = "necesary" and right = "necessary" + or + wrong = "necessiate" and right = "necessitate" + or + wrong = "neglible" and right = "negligible" + or + wrong = "negligable" and right = "negligible" + or + wrong = "negociate" and right = "negotiate" + or + wrong = "negociation" and right = "negotiation" + or + wrong = "negociations" and right = "negotiations" + or + wrong = "negotation" and right = "negotiation" + or + wrong = "neice" and right = "nice" + or + wrong = "neice" and right = "niece" + or + wrong = "neigborhood" and right = "neighborhood" + or + wrong = "neigbour" and right = "neighbor" + or + wrong = "neigbour" and right = "neighbour" + or + wrong = "neigbourhood" and right = "neighbourhood" + or + wrong = "neigbouring" and right = "neighboring" + or + wrong = "neigbouring" and right = "neighbouring" + or + wrong = "neigbours" and right = "neighbors" + or + wrong = "neigbours" and right = "neighbours" + or + wrong = "neolitic" and right = "neolithic" + or + wrong = "nessasarily" and right = "necessarily" + or + wrong = "nessecary" and right = "necessary" + or + wrong = "nestin" and right = "nesting" + or + wrong = "neverthless" and right = "nevertheless" + or + wrong = "newletters" and right = "newsletters" + or + wrong = "nickle" and right = "nickel" + or + wrong = "nightime" and right = "nighttime" + or + wrong = "nineth" and right = "ninth" + or + wrong = "ninteenth" and right = "nineteenth" + or + wrong = "ninty" and right = "ninety" + or + wrong = "nkow" and right = "know" + or + wrong = "nkwo" and right = "know" + or + wrong = "nmae" and right = "name" + or + wrong = "noncombatents" and right = "noncombatants" + or + wrong = "nonsence" and right = "nonsense" + or + wrong = "nontheless" and right = "nonetheless" + or + wrong = "norhern" and right = "northern" + or + wrong = "northen" and right = "northern" + or + wrong = "northereastern" and right = "northeastern" + or + wrong = "notabley" and right = "notably" + or + wrong = "noteable" and right = "notable" + or + wrong = "noteably" and right = "notably" + or + wrong = "noteriety" and right = "notoriety" + or + wrong = "noth" and right = "north" + or + wrong = "nothern" and right = "northern" + or + wrong = "noticable" and right = "noticeable" + or + wrong = "noticably" and right = "noticeably" + or + wrong = "notications" and right = "notifications" + or + wrong = "noticeing" and right = "noticing" + or + wrong = "noticible" and right = "noticeable" + or + wrong = "notwhithstanding" and right = "notwithstanding" + or + wrong = "noveau" and right = "nouveau" + or + wrong = "novermber" and right = "november" + or + wrong = "nowdays" and right = "nowadays" + or + wrong = "nowe" and right = "now" + or + wrong = "nto" and right = "not" + or + wrong = "nubmer" and right = "number" + or + wrong = "nucular" and right = "nuclear" + or + wrong = "nuculear" and right = "nuclear" + or + wrong = "nuisanse" and right = "nuisance" + or + wrong = "nullabour" and right = "nullarbor" + or + wrong = "numberous" and right = "numerous" + or + wrong = "nuremburg" and right = "nuremberg" + or + wrong = "nusance" and right = "nuisance" + or + wrong = "nutritent" and right = "nutrient" + or + wrong = "nutritents" and right = "nutrients" + or + wrong = "nuturing" and right = "nurturing" + or + wrong = "obect" and right = "object" + or + wrong = "obediance" and right = "obedience" + or + wrong = "obediant" and right = "obedient" + or + wrong = "obejct" and right = "object" + or + wrong = "obession" and right = "obsession" + or + wrong = "obssessed" and right = "obsessed" + or + wrong = "obstacal" and right = "obstacle" + or + wrong = "obstancles" and right = "obstacles" + or + wrong = "obstruced" and right = "obstructed" + or + wrong = "ocasion" and right = "occasion" + or + wrong = "ocasional" and right = "occasional" + or + wrong = "ocasionally" and right = "occasionally" + or + wrong = "ocasionaly" and right = "occasionally" + or + wrong = "ocasioned" and right = "occasioned" + or + wrong = "ocasions" and right = "occasions" + or + wrong = "ocassion" and right = "occasion" + or + wrong = "ocassional" and right = "occasional" + or + wrong = "ocassionally" and right = "occasionally" + or + wrong = "ocassionaly" and right = "occasionally" + or + wrong = "ocassioned" and right = "occasioned" + or + wrong = "ocassions" and right = "occasions" + or + wrong = "occaison" and right = "occasion" + or + wrong = "occassion" and right = "occasion" + or + wrong = "occassional" and right = "occasional" + or + wrong = "occassionally" and right = "occasionally" + or + wrong = "occassionaly" and right = "occasionally" + or + wrong = "occassioned" and right = "occasioned" + or + wrong = "occassions" and right = "occasions" + or + wrong = "occationally" and right = "occasionally" + or + wrong = "occour" and right = "occur" + or + wrong = "occurance" and right = "occurrence" + or + wrong = "occurances" and right = "occurrences" + or + wrong = "occured" and right = "occurred" + or + wrong = "occurence" and right = "occurrence" + or + wrong = "occurences" and right = "occurrences" + or + wrong = "occuring" and right = "occurring" + or + wrong = "occurr" and right = "occur" + or + wrong = "occurrance" and right = "occurrence" + or + wrong = "occurrances" and right = "occurrences" + or + wrong = "octohedra" and right = "octahedra" + or + wrong = "octohedral" and right = "octahedral" + or + wrong = "octohedron" and right = "octahedron" + or + wrong = "ocuntries" and right = "countries" + or + wrong = "ocuntry" and right = "country" + or + wrong = "ocurr" and right = "occur" + or + wrong = "ocurrance" and right = "occurrence" + or + wrong = "ocurred" and right = "occurred" + or + wrong = "ocurrence" and right = "occurrence" + or + wrong = "offcers" and right = "officers" + or + wrong = "offcially" and right = "officially" + or + wrong = "offereings" and right = "offerings" + or + wrong = "offical" and right = "official" + or + wrong = "offically" and right = "officially" + or + wrong = "officals" and right = "officials" + or + wrong = "officaly" and right = "officially" + or + wrong = "officialy" and right = "officially" + or + wrong = "offred" and right = "offered" + or + wrong = "oftenly" and right = "often" + or + wrong = "oging" and right = "going" + or + wrong = "oging" and right = "ogling" + or + wrong = "oject" and right = "object" + or + wrong = "omision" and right = "omission" + or + wrong = "omited" and right = "omitted" + or + wrong = "omiting" and right = "omitting" + or + wrong = "omlette" and right = "omelette" + or + wrong = "ommision" and right = "omission" + or + wrong = "ommited" and right = "omitted" + or + wrong = "ommiting" and right = "omitting" + or + wrong = "ommitted" and right = "omitted" + or + wrong = "ommitting" and right = "omitting" + or + wrong = "omniverous" and right = "omnivorous" + or + wrong = "omniverously" and right = "omnivorously" + or + wrong = "omre" and right = "more" + or + wrong = "onot" and right = "not" + or + wrong = "onot" and right = "note" + or + wrong = "onyl" and right = "only" + or + wrong = "openess" and right = "openness" + or + wrong = "oponent" and right = "opponent" + or + wrong = "oportunity" and right = "opportunity" + or + wrong = "opose" and right = "oppose" + or + wrong = "oposite" and right = "opposite" + or + wrong = "oposition" and right = "opposition" + or + wrong = "oppenly" and right = "openly" + or + wrong = "oppinion" and right = "opinion" + or + wrong = "opponant" and right = "opponent" + or + wrong = "oppononent" and right = "opponent" + or + wrong = "oppositition" and right = "opposition" + or + wrong = "oppossed" and right = "opposed" + or + wrong = "opprotunity" and right = "opportunity" + or + wrong = "opression" and right = "oppression" + or + wrong = "opressive" and right = "oppressive" + or + wrong = "opthalmic" and right = "ophthalmic" + or + wrong = "opthalmologist" and right = "ophthalmologist" + or + wrong = "opthalmology" and right = "ophthalmology" + or + wrong = "opthamologist" and right = "ophthalmologist" + or + wrong = "optmizations" and right = "optimizations" + or + wrong = "optomism" and right = "optimism" + or + wrong = "orded" and right = "ordered" + or + wrong = "organim" and right = "organism" + or + wrong = "organistion" and right = "organisation" + or + wrong = "organiztion" and right = "organization" + or + wrong = "orgin" and right = "organ" + or + wrong = "orgin" and right = "origin" + or + wrong = "orginal" and right = "original" + or + wrong = "orginally" and right = "originally" + or + wrong = "orginize" and right = "organise" + or + wrong = "oridinarily" and right = "ordinarily" + or + wrong = "origanaly" and right = "originally" + or + wrong = "originall" and right = "original" + or + wrong = "originall" and right = "originally" + or + wrong = "originaly" and right = "originally" + or + wrong = "originially" and right = "originally" + or + wrong = "originnally" and right = "originally" + or + wrong = "origional" and right = "original" + or + wrong = "orignally" and right = "originally" + or + wrong = "orignially" and right = "originally" + or + wrong = "otehr" and right = "other" + or + wrong = "oublisher" and right = "publisher" + or + wrong = "ouevre" and right = "oeuvre" + or + wrong = "ouput" and right = "output" + or + wrong = "oustanding" and right = "outstanding" + or + wrong = "overriden" and right = "overridden" + or + wrong = "overshaddowed" and right = "overshadowed" + or + wrong = "overwelming" and right = "overwhelming" + or + wrong = "overwheliming" and right = "overwhelming" + or + wrong = "owrk" and right = "work" + or + wrong = "owudl" and right = "would" + or + wrong = "oxigen" and right = "oxygen" + or + wrong = "oximoron" and right = "oxymoron" + or + wrong = "p0enis" and right = "penis" + or + wrong = "paide" and right = "paid" + or + wrong = "paitience" and right = "patience" + or + wrong = "palce" and right = "palace" + or + wrong = "palce" and right = "place" + or + wrong = "paleolitic" and right = "paleolithic" + or + wrong = "paliamentarian" and right = "parliamentarian" + or + wrong = "palistian" and right = "palestinian" + or + wrong = "palistinian" and right = "palestinian" + or + wrong = "palistinians" and right = "palestinians" + or + wrong = "pallete" and right = "palette" + or + wrong = "pamflet" and right = "pamphlet" + or + wrong = "pamplet" and right = "pamphlet" + or + wrong = "pantomine" and right = "pantomime" + or + wrong = "papanicalou" and right = "papanicolaou" + or + wrong = "paralel" and right = "parallel" + or + wrong = "paralell" and right = "parallel" + or + wrong = "paralelly" and right = "parallelly" + or + wrong = "paralely" and right = "parallelly" + or + wrong = "parallely" and right = "parallelly" + or + wrong = "paramater" and right = "parameter" + or + wrong = "paramters" and right = "parameters" + or + wrong = "parametarized" and right = "parameterized" + or + wrong = "paranthesis" and right = "parenthesis" + or + wrong = "paraphenalia" and right = "paraphernalia" + or + wrong = "parellels" and right = "parallels" + or + wrong = "parisitic" and right = "parasitic" + or + wrong = "parituclar" and right = "particular" + or + wrong = "parliment" and right = "parliament" + or + wrong = "parrakeets" and right = "parakeets" + or + wrong = "parralel" and right = "parallel" + or + wrong = "parrallel" and right = "parallel" + or + wrong = "parrallell" and right = "parallel" + or + wrong = "parrallelly" and right = "parallelly" + or + wrong = "parrallely" and right = "parallelly" + or + wrong = "partialy" and right = "partially" + or + wrong = "particually" and right = "particularly" + or + wrong = "particualr" and right = "particular" + or + wrong = "particuarly" and right = "particularly" + or + wrong = "particularily" and right = "particularly" + or + wrong = "particulary" and right = "particularly" + or + wrong = "pary" and right = "party" + or + wrong = "pased" and right = "passed" + or + wrong = "pasengers" and right = "passengers" + or + wrong = "passerbys" and right = "passersby" + or + wrong = "pasttime" and right = "pastime" + or + wrong = "pastural" and right = "pastoral" + or + wrong = "paticular" and right = "particular" + or + wrong = "pattented" and right = "patented" + or + wrong = "pavillion" and right = "pavilion" + or + wrong = "payed" and right = "paid" + or + wrong = "pblisher" and right = "publisher" + or + wrong = "pbulisher" and right = "publisher" + or + wrong = "peageant" and right = "pageant" + or + wrong = "peaple" and right = "people" + or + wrong = "peaples" and right = "peoples" + or + wrong = "peculure" and right = "peculiar" + or + wrong = "pedestrain" and right = "pedestrian" + or + wrong = "peformed" and right = "performed" + or + wrong = "peice" and right = "piece" + or + wrong = "peloponnes" and right = "peloponnesus" + or + wrong = "penatly" and right = "penalty" + or + wrong = "penerator" and right = "penetrator" + or + wrong = "penisula" and right = "peninsula" + or + wrong = "penisular" and right = "peninsular" + or + wrong = "penninsula" and right = "peninsula" + or + wrong = "penninsular" and right = "peninsular" + or + wrong = "pennisula" and right = "peninsula" + or + wrong = "pennyslvania" and right = "pennsylvania" + or + wrong = "pensinula" and right = "peninsula" + or + wrong = "pensle" and right = "pencil" + or + wrong = "peom" and right = "poem" + or + wrong = "peoms" and right = "poems" + or + wrong = "peopel" and right = "people" + or + wrong = "peopels" and right = "peoples" + or + wrong = "peotry" and right = "poetry" + or + wrong = "perade" and right = "parade" + or + wrong = "percepted" and right = "perceived" + or + wrong = "percieve" and right = "perceive" + or + wrong = "percieved" and right = "perceived" + or + wrong = "perenially" and right = "perennially" + or + wrong = "peretrator" and right = "perpetrator" + or + wrong = "perfomance" and right = "performance" + or + wrong = "perfomers" and right = "performers" + or + wrong = "performence" and right = "performance" + or + wrong = "performes" and right = "performed" + or + wrong = "performes" and right = "performs" + or + wrong = "perhasp" and right = "perhaps" + or + wrong = "perheaps" and right = "perhaps" + or + wrong = "perhpas" and right = "perhaps" + or + wrong = "peripathetic" and right = "peripatetic" + or + wrong = "peristent" and right = "persistent" + or + wrong = "perjery" and right = "perjury" + or + wrong = "perjorative" and right = "pejorative" + or + wrong = "permanant" and right = "permanent" + or + wrong = "permenant" and right = "permanent" + or + wrong = "permenantly" and right = "permanently" + or + wrong = "permissable" and right = "permissible" + or + wrong = "perogative" and right = "prerogative" + or + wrong = "peronal" and right = "personal" + or + wrong = "perosnality" and right = "personality" + or + wrong = "perpertrated" and right = "perpetrated" + or + wrong = "perphas" and right = "perhaps" + or + wrong = "perpindicular" and right = "perpendicular" + or + wrong = "persan" and right = "person" + or + wrong = "perseverence" and right = "perseverance" + or + wrong = "persistance" and right = "persistence" + or + wrong = "persistant" and right = "persistent" + or + wrong = "personel" and right = "personal" + or + wrong = "personel" and right = "personnel" + or + wrong = "personell" and right = "personnel" + or + wrong = "personnell" and right = "personnel" + or + wrong = "persuded" and right = "persuaded" + or + wrong = "persue" and right = "pursue" + or + wrong = "persued" and right = "pursued" + or + wrong = "persuing" and right = "pursuing" + or + wrong = "persuit" and right = "pursuit" + or + wrong = "persuits" and right = "pursuits" + or + wrong = "pertubation" and right = "perturbation" + or + wrong = "pertubations" and right = "perturbations" + or + wrong = "pessiary" and right = "pessary" + or + wrong = "petetion" and right = "petition" + or + wrong = "pharoah" and right = "pharaoh" + or + wrong = "phenomenom" and right = "phenomenon" + or + wrong = "phenomenonal" and right = "phenomenal" + or + wrong = "phenomenonly" and right = "phenomenally" + or + wrong = "phenomonenon" and right = "phenomenon" + or + wrong = "phenomonon" and right = "phenomenon" + or + wrong = "phenonmena" and right = "phenomena" + or + wrong = "philipines" and right = "philippines" + or + wrong = "philisopher" and right = "philosopher" + or + wrong = "philisophical" and right = "philosophical" + or + wrong = "philisophy" and right = "philosophy" + or + wrong = "phillipine" and right = "philippine" + or + wrong = "phillipines" and right = "philippines" + or + wrong = "phillippines" and right = "philippines" + or + wrong = "phillosophically" and right = "philosophically" + or + wrong = "philospher" and right = "philosopher" + or + wrong = "philosphies" and right = "philosophies" + or + wrong = "philosphy" and right = "philosophy" + or + wrong = "phonecian" and right = "phoenecian" + or + wrong = "phongraph" and right = "phonograph" + or + wrong = "phylosophical" and right = "philosophical" + or + wrong = "physicaly" and right = "physically" + or + wrong = "piblisher" and right = "publisher" + or + wrong = "pich" and right = "pitch" + or + wrong = "pilgrimmage" and right = "pilgrimage" + or + wrong = "pilgrimmages" and right = "pilgrimages" + or + wrong = "pinapple" and right = "pineapple" + or + wrong = "pinnaple" and right = "pineapple" + or + wrong = "pinoneered" and right = "pioneered" + or + wrong = "plagarism" and right = "plagiarism" + or + wrong = "planation" and right = "plantation" + or + wrong = "planed" and right = "planned" + or + wrong = "plantiff" and right = "plaintiff" + or + wrong = "plateu" and right = "plateau" + or + wrong = "plausable" and right = "plausible" + or + wrong = "playright" and right = "playwright" + or + wrong = "playwrite" and right = "playwright" + or + wrong = "playwrites" and right = "playwrights" + or + wrong = "pleasent" and right = "pleasant" + or + wrong = "plebicite" and right = "plebiscite" + or + wrong = "plesant" and right = "pleasant" + or + wrong = "poenis" and right = "penis" + or + wrong = "poeoples" and right = "peoples" + or + wrong = "poety" and right = "poetry" + or + wrong = "poisin" and right = "poison" + or + wrong = "polical" and right = "political" + or + wrong = "polinator" and right = "pollinator" + or + wrong = "polinators" and right = "pollinators" + or + wrong = "politican" and right = "politician" + or + wrong = "politicans" and right = "politicians" + or + wrong = "poltical" and right = "political" + or + wrong = "polute" and right = "pollute" + or + wrong = "poluted" and right = "polluted" + or + wrong = "polutes" and right = "pollutes" + or + wrong = "poluting" and right = "polluting" + or + wrong = "polution" and right = "pollution" + or + wrong = "polyphonyic" and right = "polyphonic" + or + wrong = "polysaccaride" and right = "polysaccharide" + or + wrong = "polysaccharid" and right = "polysaccharide" + or + wrong = "pomegranite" and right = "pomegranate" + or + wrong = "pomotion" and right = "promotion" + or + wrong = "poportional" and right = "proportional" + or + wrong = "popoulation" and right = "population" + or + wrong = "popularaty" and right = "popularity" + or + wrong = "populare" and right = "popular" + or + wrong = "populer" and right = "popular" + or + wrong = "porshan" and right = "portion" + or + wrong = "porshon" and right = "portion" + or + wrong = "portait" and right = "portrait" + or + wrong = "portayed" and right = "portrayed" + or + wrong = "portraing" and right = "portraying" + or + wrong = "portugese" and right = "portuguese" + or + wrong = "portuguease" and right = "portuguese" + or + wrong = "portugues" and right = "portuguese" + or + wrong = "posess" and right = "possess" + or + wrong = "posessed" and right = "possessed" + or + wrong = "posesses" and right = "possesses" + or + wrong = "posessing" and right = "possessing" + or + wrong = "posession" and right = "possession" + or + wrong = "posessions" and right = "possessions" + or + wrong = "posion" and right = "poison" + or + wrong = "positon" and right = "position" + or + wrong = "positon" and right = "positron" + or + wrong = "possable" and right = "possible" + or + wrong = "possably" and right = "possibly" + or + wrong = "posseses" and right = "possesses" + or + wrong = "possesing" and right = "possessing" + or + wrong = "possesion" and right = "possession" + or + wrong = "possessess" and right = "possesses" + or + wrong = "possibile" and right = "possible" + or + wrong = "possibilty" and right = "possibility" + or + wrong = "possiblility" and right = "possibility" + or + wrong = "possiblilty" and right = "possibility" + or + wrong = "possiblities" and right = "possibilities" + or + wrong = "possiblity" and right = "possibility" + or + wrong = "possition" and right = "position" + or + wrong = "postdam" and right = "potsdam" + or + wrong = "posthomous" and right = "posthumous" + or + wrong = "postion" and right = "position" + or + wrong = "postive" and right = "positive" + or + wrong = "potatos" and right = "potatoes" + or + wrong = "potrait" and right = "portrait" + or + wrong = "potrayed" and right = "portrayed" + or + wrong = "poulations" and right = "populations" + or + wrong = "poverful" and right = "powerful" + or + wrong = "poweful" and right = "powerful" + or + wrong = "powerfull" and right = "powerful" + or + wrong = "ppublisher" and right = "publisher" + or + wrong = "practial" and right = "practical" + or + wrong = "practially" and right = "practically" + or + wrong = "practicaly" and right = "practically" + or + wrong = "practicioner" and right = "practitioner" + or + wrong = "practicioners" and right = "practitioners" + or + wrong = "practicly" and right = "practically" + or + wrong = "practioner" and right = "practitioner" + or + wrong = "practioners" and right = "practitioners" + or + wrong = "prairy" and right = "prairie" + or + wrong = "prarie" and right = "prairie" + or + wrong = "praries" and right = "prairies" + or + wrong = "pratice" and right = "practice" + or + wrong = "preample" and right = "preamble" + or + wrong = "precedessor" and right = "predecessor" + or + wrong = "preceed" and right = "precede" + or + wrong = "preceeded" and right = "preceded" + or + wrong = "preceeding" and right = "preceding" + or + wrong = "preceeds" and right = "precedes" + or + wrong = "precendence" and right = "precedence" + or + wrong = "precentage" and right = "percentage" + or + wrong = "precice" and right = "precise" + or + wrong = "precisly" and right = "precisely" + or + wrong = "precurser" and right = "precursor" + or + wrong = "predecesors" and right = "predecessors" + or + wrong = "predicatble" and right = "predictable" + or + wrong = "predicitons" and right = "predictions" + or + wrong = "predomiantly" and right = "predominately" + or + wrong = "prefered" and right = "preferred" + or + wrong = "prefering" and right = "preferring" + or + wrong = "preferrably" and right = "preferably" + or + wrong = "pregancies" and right = "pregnancies" + or + wrong = "preiod" and right = "period" + or + wrong = "preliferation" and right = "proliferation" + or + wrong = "premeire" and right = "premiere" + or + wrong = "premeired" and right = "premiered" + or + wrong = "premillenial" and right = "premillennial" + or + wrong = "preminence" and right = "preeminence" + or + wrong = "premission" and right = "permission" + or + wrong = "premonasterians" and right = "premonstratensians" + or + wrong = "preocupation" and right = "preoccupation" + or + wrong = "prepair" and right = "prepare" + or + wrong = "prepartion" and right = "preparation" + or + wrong = "prepatory" and right = "preparatory" + or + wrong = "preperation" and right = "preparation" + or + wrong = "preperations" and right = "preparations" + or + wrong = "preriod" and right = "period" + or + wrong = "presedential" and right = "presidential" + or + wrong = "presense" and right = "presence" + or + wrong = "presidenital" and right = "presidential" + or + wrong = "presidental" and right = "presidential" + or + wrong = "presitgious" and right = "prestigious" + or + wrong = "prespective" and right = "perspective" + or + wrong = "prestigeous" and right = "prestigious" + or + wrong = "prestigous" and right = "prestigious" + or + wrong = "presumabely" and right = "presumably" + or + wrong = "presumibly" and right = "presumably" + or + wrong = "pretection" and right = "protection" + or + wrong = "prevelant" and right = "prevalent" + or + wrong = "preverse" and right = "perverse" + or + wrong = "previvous" and right = "previous" + or + wrong = "pricipal" and right = "principal" + or + wrong = "priciple" and right = "principle" + or + wrong = "priestood" and right = "priesthood" + or + wrong = "primarly" and right = "primarily" + or + wrong = "primative" and right = "primitive" + or + wrong = "primatively" and right = "primitively" + or + wrong = "primatives" and right = "primitives" + or + wrong = "primordal" and right = "primordial" + or + wrong = "principaly" and right = "principality" + or + wrong = "principial" and right = "principal" + or + wrong = "principlaity" and right = "principality" + or + wrong = "principly" and right = "principally" + or + wrong = "prinicipal" and right = "principal" + or + wrong = "privalege" and right = "privilege" + or + wrong = "privaleges" and right = "privileges" + or + wrong = "priveledges" and right = "privileges" + or + wrong = "privelege" and right = "privilege" + or + wrong = "priveleged" and right = "privileged" + or + wrong = "priveleges" and right = "privileges" + or + wrong = "privelige" and right = "privilege" + or + wrong = "priveliged" and right = "privileged" + or + wrong = "priveliges" and right = "privileges" + or + wrong = "privelleges" and right = "privileges" + or + wrong = "privilage" and right = "privilege" + or + wrong = "priviledge" and right = "privilege" + or + wrong = "priviledges" and right = "privileges" + or + wrong = "privledge" and right = "privilege" + or + wrong = "privte" and right = "private" + or + wrong = "probabilaty" and right = "probability" + or + wrong = "probablistic" and right = "probabilistic" + or + wrong = "probablly" and right = "probably" + or + wrong = "probalibity" and right = "probability" + or + wrong = "probaly" and right = "probably" + or + wrong = "probelm" and right = "problem" + or + wrong = "proccess" and right = "process" + or + wrong = "proccessing" and right = "processing" + or + wrong = "procede" and right = "precede" + or + wrong = "procede" and right = "proceed" + or + wrong = "proceded" and right = "preceded" + or + wrong = "proceded" and right = "proceeded" + or + wrong = "procedes" and right = "precedes" + or + wrong = "procedes" and right = "proceeds" + or + wrong = "procedger" and right = "procedure" + or + wrong = "proceding" and right = "preceding" + or + wrong = "proceding" and right = "proceeding" + or + wrong = "procedings" and right = "proceedings" + or + wrong = "proceedure" and right = "procedure" + or + wrong = "proces" and right = "process" + or + wrong = "procesed" and right = "processed" + or + wrong = "processer" and right = "processor" + or + wrong = "proclaimation" and right = "proclamation" + or + wrong = "proclamed" and right = "proclaimed" + or + wrong = "proclaming" and right = "proclaiming" + or + wrong = "proclomation" and right = "proclamation" + or + wrong = "profesion" and right = "profession" + or + wrong = "profesion" and right = "profusion" + or + wrong = "profesor" and right = "professor" + or + wrong = "professer" and right = "professor" + or + wrong = "proffesed" and right = "professed" + or + wrong = "proffesion" and right = "profession" + or + wrong = "proffesional" and right = "professional" + or + wrong = "proffesor" and right = "professor" + or + wrong = "profilic" and right = "prolific" + or + wrong = "progessed" and right = "progressed" + or + wrong = "progidy" and right = "prodigy" + or + wrong = "programable" and right = "programmable" + or + wrong = "progrom" and right = "pogrom" + or + wrong = "progrom" and right = "program" + or + wrong = "progroms" and right = "pogroms" + or + wrong = "progroms" and right = "programs" + or + wrong = "prohabition" and right = "prohibition" + or + wrong = "prologomena" and right = "prolegomena" + or + wrong = "prominance" and right = "prominence" + or + wrong = "prominant" and right = "prominent" + or + wrong = "prominantly" and right = "prominently" + or + wrong = "prominately" and right = "predominately" + or + wrong = "prominately" and right = "prominently" + or + wrong = "promiscous" and right = "promiscuous" + or + wrong = "promotted" and right = "promoted" + or + wrong = "pronomial" and right = "pronominal" + or + wrong = "pronouced" and right = "pronounced" + or + wrong = "pronounched" and right = "pronounced" + or + wrong = "pronounciation" and right = "pronunciation" + or + wrong = "proove" and right = "prove" + or + wrong = "prooved" and right = "proved" + or + wrong = "prophacy" and right = "prophecy" + or + wrong = "propietary" and right = "proprietary" + or + wrong = "propmted" and right = "prompted" + or + wrong = "propoganda" and right = "propaganda" + or + wrong = "propogate" and right = "propagate" + or + wrong = "propogates" and right = "propagates" + or + wrong = "propogation" and right = "propagation" + or + wrong = "propostion" and right = "proposition" + or + wrong = "propotions" and right = "proportions" + or + wrong = "propper" and right = "proper" + or + wrong = "propperly" and right = "properly" + or + wrong = "proprietory" and right = "proprietary" + or + wrong = "proseletyzing" and right = "proselytizing" + or + wrong = "protaganist" and right = "protagonist" + or + wrong = "protaganists" and right = "protagonists" + or + wrong = "protocal" and right = "protocol" + or + wrong = "protoganist" and right = "protagonist" + or + wrong = "prototpe" and right = "prototype" + or + wrong = "protoype" and right = "prototype" + or + wrong = "protrayed" and right = "portrayed" + or + wrong = "protruberance" and right = "protuberance" + or + wrong = "protruberances" and right = "protuberances" + or + wrong = "prouncements" and right = "pronouncements" + or + wrong = "provacative" and right = "provocative" + or + wrong = "provded" and right = "provided" + or + wrong = "provicial" and right = "provincial" + or + wrong = "provinicial" and right = "provincial" + or + wrong = "provisiosn" and right = "provision" + or + wrong = "provisonal" and right = "provisional" + or + wrong = "proximty" and right = "proximity" + or + wrong = "pseudononymous" and right = "pseudonymous" + or + wrong = "pseudonyn" and right = "pseudonym" + or + wrong = "psuedo" and right = "pseudo" + or + wrong = "psycology" and right = "psychology" + or + wrong = "psyhic" and right = "psychic" + or + wrong = "pubilsher" and right = "publisher" + or + wrong = "pubisher" and right = "publisher" + or + wrong = "publiaher" and right = "publisher" + or + wrong = "publically" and right = "publicly" + or + wrong = "publicaly" and right = "publicly" + or + wrong = "publicher" and right = "publisher" + or + wrong = "publihser" and right = "publisher" + or + wrong = "publisehr" and right = "publisher" + or + wrong = "publiser" and right = "publisher" + or + wrong = "publisger" and right = "publisher" + or + wrong = "publisheed" and right = "published" + or + wrong = "publisherr" and right = "publisher" + or + wrong = "publishher" and right = "publisher" + or + wrong = "publishor" and right = "publisher" + or + wrong = "publishre" and right = "publisher" + or + wrong = "publissher" and right = "publisher" + or + wrong = "publlisher" and right = "publisher" + or + wrong = "publsiher" and right = "publisher" + or + wrong = "publusher" and right = "publisher" + or + wrong = "puchasing" and right = "purchasing" + or + wrong = "pucini" and right = "puccini" + or + wrong = "pulisher" and right = "publisher" + or + wrong = "pumkin" and right = "pumpkin" + or + wrong = "puplisher" and right = "publisher" + or + wrong = "puritannical" and right = "puritanical" + or + wrong = "purposedly" and right = "purposely" + or + wrong = "purpotedly" and right = "purportedly" + or + wrong = "pursuade" and right = "persuade" + or + wrong = "pursuaded" and right = "persuaded" + or + wrong = "pursuades" and right = "persuades" + or + wrong = "pususading" and right = "persuading" + or + wrong = "puting" and right = "putting" + or + wrong = "pwoer" and right = "power" + or + wrong = "pyscic" and right = "psychic" + or + wrong = "qtuie" and right = "quiet" + or + wrong = "qtuie" and right = "quite" + or + wrong = "quantaty" and right = "quantity" + or + wrong = "quantitiy" and right = "quantity" + or + wrong = "quarantaine" and right = "quarantine" + or + wrong = "queenland" and right = "queensland" + or + wrong = "questonable" and right = "questionable" + or + wrong = "quicklyu" and right = "quickly" + or + wrong = "quinessential" and right = "quintessential" + or + wrong = "quitted" and right = "quit" + or + wrong = "quizes" and right = "quizzes" + or + wrong = "qutie" and right = "quiet" + or + wrong = "qutie" and right = "quite" + or + wrong = "rabinnical" and right = "rabbinical" + or + wrong = "racaus" and right = "raucous" + or + wrong = "radiactive" and right = "radioactive" + or + wrong = "radify" and right = "ratify" + or + wrong = "raelly" and right = "really" + or + wrong = "rarified" and right = "rarefied" + or + wrong = "reaccurring" and right = "recurring" + or + wrong = "reacing" and right = "reaching" + or + wrong = "reacll" and right = "recall" + or + wrong = "readmition" and right = "readmission" + or + wrong = "realitvely" and right = "relatively" + or + wrong = "realsitic" and right = "realistic" + or + wrong = "realtions" and right = "relations" + or + wrong = "realy" and right = "really" + or + wrong = "realyl" and right = "really" + or + wrong = "reasearch" and right = "research" + or + wrong = "rebiulding" and right = "rebuilding" + or + wrong = "rebllions" and right = "rebellions" + or + wrong = "rebounce" and right = "rebound" + or + wrong = "reccomend" and right = "recommend" + or + wrong = "reccomendations" and right = "recommendations" + or + wrong = "reccomended" and right = "recommended" + or + wrong = "reccomending" and right = "recommending" + or + wrong = "reccommend" and right = "recommend" + or + wrong = "reccommended" and right = "recommended" + or + wrong = "reccommending" and right = "recommending" + or + wrong = "reccuring" and right = "recurring" + or + wrong = "receeded" and right = "receded" + or + wrong = "receeding" and right = "receding" + or + wrong = "recepient" and right = "recipient" + or + wrong = "recepients" and right = "recipients" + or + wrong = "receving" and right = "receiving" + or + wrong = "rechargable" and right = "rechargeable" + or + wrong = "reched" and right = "reached" + or + wrong = "recide" and right = "reside" + or + wrong = "recided" and right = "resided" + or + wrong = "recident" and right = "resident" + or + wrong = "recidents" and right = "residents" + or + wrong = "reciding" and right = "residing" + or + wrong = "reciepents" and right = "recipients" + or + wrong = "reciept" and right = "receipt" + or + wrong = "recieve" and right = "receive" + or + wrong = "recieved" and right = "received" + or + wrong = "reciever" and right = "receiver" + or + wrong = "recievers" and right = "receivers" + or + wrong = "recieves" and right = "receives" + or + wrong = "recieving" and right = "receiving" + or + wrong = "recipiant" and right = "recipient" + or + wrong = "recipiants" and right = "recipients" + or + wrong = "recived" and right = "received" + or + wrong = "recivership" and right = "receivership" + or + wrong = "recogise" and right = "recognise" + or + wrong = "recogize" and right = "recognize" + or + wrong = "recomend" and right = "recommend" + or + wrong = "recomended" and right = "recommended" + or + wrong = "recomending" and right = "recommending" + or + wrong = "recomends" and right = "recommends" + or + wrong = "recommedations" and right = "recommendations" + or + wrong = "recompence" and right = "recompense" + or + wrong = "reconaissance" and right = "reconnaissance" + or + wrong = "reconcilation" and right = "reconciliation" + or + wrong = "reconized" and right = "recognized" + or + wrong = "reconnaisance" and right = "reconnaissance" + or + wrong = "reconnaissence" and right = "reconnaissance" + or + wrong = "recontructed" and right = "reconstructed" + or + wrong = "recquired" and right = "required" + or + wrong = "recrational" and right = "recreational" + or + wrong = "recrod" and right = "record" + or + wrong = "recuiting" and right = "recruiting" + or + wrong = "recuring" and right = "recurring" + or + wrong = "recurrance" and right = "recurrence" + or + wrong = "rediculous" and right = "ridiculous" + or + wrong = "reedeming" and right = "redeeming" + or + wrong = "reenforced" and right = "reinforced" + or + wrong = "refect" and right = "reflect" + or + wrong = "refedendum" and right = "referendum" + or + wrong = "referal" and right = "referral" + or + wrong = "referece" and right = "reference" + or + wrong = "refereces" and right = "references" + or + wrong = "refered" and right = "referred" + or + wrong = "referemce" and right = "reference" + or + wrong = "referemces" and right = "references" + or + wrong = "referencs" and right = "references" + or + wrong = "referenece" and right = "reference" + or + wrong = "refereneced" and right = "referenced" + or + wrong = "refereneces" and right = "references" + or + wrong = "referiang" and right = "referring" + or + wrong = "refering" and right = "referring" + or + wrong = "refernce" and right = "reference" + or + wrong = "refernce" and right = "references" + or + wrong = "refernces" and right = "references" + or + wrong = "referrence" and right = "reference" + or + wrong = "referrences" and right = "references" + or + wrong = "referrs" and right = "refers" + or + wrong = "reffered" and right = "referred" + or + wrong = "refference" and right = "reference" + or + wrong = "reffering" and right = "referring" + or + wrong = "refrence" and right = "reference" + or + wrong = "refrences" and right = "references" + or + wrong = "refrers" and right = "refers" + or + wrong = "refridgeration" and right = "refrigeration" + or + wrong = "refridgerator" and right = "refrigerator" + or + wrong = "refromist" and right = "reformist" + or + wrong = "refusla" and right = "refusal" + or + wrong = "regardes" and right = "regards" + or + wrong = "regluar" and right = "regular" + or + wrong = "reguarly" and right = "regularly" + or + wrong = "regulaion" and right = "regulation" + or + wrong = "regulaotrs" and right = "regulators" + or + wrong = "regularily" and right = "regularly" + or + wrong = "rehersal" and right = "rehearsal" + or + wrong = "reicarnation" and right = "reincarnation" + or + wrong = "reigining" and right = "reigning" + or + wrong = "reknown" and right = "renown" + or + wrong = "reknowned" and right = "renowned" + or + wrong = "rela" and right = "real" + or + wrong = "relaly" and right = "really" + or + wrong = "relatiopnship" and right = "relationship" + or + wrong = "relativly" and right = "relatively" + or + wrong = "relected" and right = "reelected" + or + wrong = "releive" and right = "relieve" + or + wrong = "releived" and right = "relieved" + or + wrong = "releiver" and right = "reliever" + or + wrong = "releses" and right = "releases" + or + wrong = "relevence" and right = "relevance" + or + wrong = "relevent" and right = "relevant" + or + wrong = "reliablity" and right = "reliability" + or + wrong = "relient" and right = "reliant" + or + wrong = "religeous" and right = "religious" + or + wrong = "religous" and right = "religious" + or + wrong = "religously" and right = "religiously" + or + wrong = "relinqushment" and right = "relinquishment" + or + wrong = "relitavely" and right = "relatively" + or + wrong = "relized" and right = "realised" + or + wrong = "relized" and right = "realized" + or + wrong = "relpacement" and right = "replacement" + or + wrong = "remaing" and right = "remaining" + or + wrong = "remeber" and right = "remember" + or + wrong = "rememberable" and right = "memorable" + or + wrong = "rememberance" and right = "remembrance" + or + wrong = "remembrence" and right = "remembrance" + or + wrong = "remenant" and right = "remnant" + or + wrong = "remenicent" and right = "reminiscent" + or + wrong = "reminent" and right = "remnant" + or + wrong = "reminescent" and right = "reminiscent" + or + wrong = "reminscent" and right = "reminiscent" + or + wrong = "reminsicent" and right = "reminiscent" + or + wrong = "rendevous" and right = "rendezvous" + or + wrong = "rendezous" and right = "rendezvous" + or + wrong = "renedered" and right = "rende" + or + wrong = "renewl" and right = "renewal" + or + wrong = "rennovate" and right = "renovate" + or + wrong = "rennovated" and right = "renovated" + or + wrong = "rennovating" and right = "renovating" + or + wrong = "rennovation" and right = "renovation" + or + wrong = "rentors" and right = "renters" + or + wrong = "reoccurrence" and right = "recurrence" + or + wrong = "reorganision" and right = "reorganisation" + or + wrong = "repatition" and right = "repartition" + or + wrong = "repatition" and right = "repetition" + or + wrong = "repblic" and right = "republic" + or + wrong = "repblican" and right = "republican" + or + wrong = "repblicans" and right = "republicans" + or + wrong = "repblics" and right = "republics" + or + wrong = "repectively" and right = "respectively" + or + wrong = "repeition" and right = "repetition" + or + wrong = "repentence" and right = "repentance" + or + wrong = "repentent" and right = "repentant" + or + wrong = "repeteadly" and right = "repeatedly" + or + wrong = "repetion" and right = "repetition" + or + wrong = "repid" and right = "rapid" + or + wrong = "reponse" and right = "response" + or + wrong = "reponsible" and right = "responsible" + or + wrong = "reportadly" and right = "reportedly" + or + wrong = "represantative" and right = "representative" + or + wrong = "representive" and right = "representative" + or + wrong = "representives" and right = "representatives" + or + wrong = "reproducable" and right = "reproducible" + or + wrong = "reprtoire" and right = "repertoire" + or + wrong = "repsectively" and right = "respectively" + or + wrong = "reptition" and right = "repetition" + or + wrong = "repubic" and right = "republic" + or + wrong = "repubican" and right = "republican" + or + wrong = "repubicans" and right = "republicans" + or + wrong = "repubics" and right = "republics" + or + wrong = "republi" and right = "republic" + or + wrong = "republian" and right = "republican" + or + wrong = "republians" and right = "republicans" + or + wrong = "republis" and right = "republics" + or + wrong = "repulic" and right = "republic" + or + wrong = "repulican" and right = "republican" + or + wrong = "repulicans" and right = "republicans" + or + wrong = "repulics" and right = "republics" + or + wrong = "requirment" and right = "requirement" + or + wrong = "requred" and right = "required" + or + wrong = "resaurant" and right = "restaurant" + or + wrong = "resembelance" and right = "resemblance" + or + wrong = "resembes" and right = "resembles" + or + wrong = "resemblence" and right = "resemblance" + or + wrong = "resevoir" and right = "reservoir" + or + wrong = "residental" and right = "residential" + or + wrong = "resignement" and right = "resignment" + or + wrong = "resistable" and right = "resistible" + or + wrong = "resistence" and right = "resistance" + or + wrong = "resistent" and right = "resistant" + or + wrong = "respectivly" and right = "respectively" + or + wrong = "responce" and right = "response" + or + wrong = "responibilities" and right = "responsibilities" + or + wrong = "responisble" and right = "responsible" + or + wrong = "responnsibilty" and right = "responsibility" + or + wrong = "responsability" and right = "responsibility" + or + wrong = "responsibile" and right = "responsible" + or + wrong = "responsibilites" and right = "responsibilities" + or + wrong = "responsiblities" and right = "responsibilities" + or + wrong = "responsiblity" and right = "responsibility" + or + wrong = "ressemblance" and right = "resemblance" + or + wrong = "ressemble" and right = "resemble" + or + wrong = "ressembled" and right = "resembled" + or + wrong = "ressemblence" and right = "resemblance" + or + wrong = "ressembling" and right = "resembling" + or + wrong = "resssurecting" and right = "resurrecting" + or + wrong = "ressurect" and right = "resurrect" + or + wrong = "ressurected" and right = "resurrected" + or + wrong = "ressurection" and right = "resurrection" + or + wrong = "ressurrection" and right = "resurrection" + or + wrong = "restarant" and right = "restaurant" + or + wrong = "restarants" and right = "restaurants" + or + wrong = "restaraunt" and right = "restaurant" + or + wrong = "restaraunteur" and right = "restaurateur" + or + wrong = "restaraunteurs" and right = "restaurateurs" + or + wrong = "restaraunts" and right = "restaurants" + or + wrong = "restauranteurs" and right = "restaurateurs" + or + wrong = "restauration" and right = "restoration" + or + wrong = "restauraunt" and right = "restaurant" + or + wrong = "resteraunt" and right = "restaurant" + or + wrong = "resteraunts" and right = "restaurants" + or + wrong = "resticted" and right = "restricted" + or + wrong = "restraunt" and right = "restaurant" + or + wrong = "restraunt" and right = "restraint" + or + wrong = "resturant" and right = "restaurant" + or + wrong = "resturants" and right = "restaurants" + or + wrong = "resturaunt" and right = "restaurant" + or + wrong = "resturaunts" and right = "restaurants" + or + wrong = "resurecting" and right = "resurrecting" + or + wrong = "retalitated" and right = "retaliated" + or + wrong = "retalitation" and right = "retaliation" + or + wrong = "retreive" and right = "retrieve" + or + wrong = "retrive" and right = "retrieve" + or + wrong = "returnd" and right = "returned" + or + wrong = "revaluated" and right = "reevaluated" + or + wrong = "reveiw" and right = "review" + or + wrong = "reveral" and right = "reversal" + or + wrong = "reversable" and right = "reversible" + or + wrong = "revolutionar" and right = "revolutionary" + or + wrong = "rewitten" and right = "rewritten" + or + wrong = "rewriet" and right = "rewrite" + or + wrong = "rference" and right = "reference" + or + wrong = "rferences" and right = "references" + or + wrong = "rhymme" and right = "rhyme" + or + wrong = "rhythem" and right = "rhythm" + or + wrong = "rhythim" and right = "rhythm" + or + wrong = "rhytmic" and right = "rhythmic" + or + wrong = "rigeur" and right = "rigor" + or + wrong = "rigeur" and right = "rigour" + or + wrong = "rigeur" and right = "rigueur" + or + wrong = "rigourous" and right = "rigorous" + or + wrong = "rininging" and right = "ringing" + or + wrong = "rised" and right = "raised" + or + wrong = "rised" and right = "rose" + or + wrong = "rockerfeller" and right = "rockefeller" + or + wrong = "rococco" and right = "rococo" + or + wrong = "rocord" and right = "record" + or + wrong = "roomate" and right = "roommate" + or + wrong = "rougly" and right = "roughly" + or + wrong = "rucuperate" and right = "recuperate" + or + wrong = "rudimentatry" and right = "rudimentary" + or + wrong = "rulle" and right = "rule" + or + wrong = "runing" and right = "running" + or + wrong = "runnung" and right = "running" + or + wrong = "russina" and right = "russian" + or + wrong = "russion" and right = "russian" + or + wrong = "rwite" and right = "write" + or + wrong = "rythem" and right = "rhythm" + or + wrong = "rythim" and right = "rhythm" + or + wrong = "rythm" and right = "rhythm" + or + wrong = "rythmic" and right = "rhythmic" + or + wrong = "rythyms" and right = "rhythms" + or + wrong = "sacrafice" and right = "sacrifice" + or + wrong = "sacreligious" and right = "sacrilegious" + or + wrong = "sacremento" and right = "sacramento" + or + wrong = "sacrifical" and right = "sacrificial" + or + wrong = "saftey" and right = "safety" + or + wrong = "safty" and right = "safety" + or + wrong = "salery" and right = "salary" + or + wrong = "sanctionning" and right = "sanctioning" + or + wrong = "sandwhich" and right = "sandwich" + or + wrong = "sanhedrim" and right = "sanhedrin" + or + wrong = "santioned" and right = "sanctioned" + or + wrong = "sargant" and right = "sergeant" + or + wrong = "sargeant" and right = "sergeant" + or + wrong = "sasy" and right = "sassy" + or + wrong = "sasy" and right = "says" + or + wrong = "satelite" and right = "satellite" + or + wrong = "satelites" and right = "satellites" + or + wrong = "saterday" and right = "saturday" + or + wrong = "saterdays" and right = "saturdays" + or + wrong = "satisfactority" and right = "satisfactorily" + or + wrong = "satric" and right = "satiric" + or + wrong = "satrical" and right = "satirical" + or + wrong = "satrically" and right = "satirically" + or + wrong = "sattelite" and right = "satellite" + or + wrong = "sattelites" and right = "satellites" + or + wrong = "saught" and right = "sought" + or + wrong = "saveing" and right = "saving" + or + wrong = "saxaphone" and right = "saxophone" + or + wrong = "scaleable" and right = "scalable" + or + wrong = "scandanavia" and right = "scandinavia" + or + wrong = "scaricity" and right = "scarcity" + or + wrong = "scavanged" and right = "scavenged" + or + wrong = "schedual" and right = "schedule" + or + wrong = "scholarhip" and right = "scholarship" + or + wrong = "scholarstic" and right = "scholarly" + or + wrong = "scholarstic" and right = "scholastic" + or + wrong = "scientfic" and right = "scientific" + or + wrong = "scientifc" and right = "scientific" + or + wrong = "scientis" and right = "scientist" + or + wrong = "scince" and right = "science" + or + wrong = "scinece" and right = "science" + or + wrong = "scirpt" and right = "script" + or + wrong = "scoll" and right = "scroll" + or + wrong = "screenwrighter" and right = "screenwriter" + or + wrong = "scrutinity" and right = "scrutiny" + or + wrong = "scuptures" and right = "sculptures" + or + wrong = "seach" and right = "search" + or + wrong = "seached" and right = "searched" + or + wrong = "seaches" and right = "searches" + or + wrong = "secceeded" and right = "seceded" + or + wrong = "secceeded" and right = "succeeded" + or + wrong = "seceed" and right = "secede" + or + wrong = "seceed" and right = "succeed" + or + wrong = "seceeded" and right = "seceded" + or + wrong = "seceeded" and right = "succeeded" + or + wrong = "secratary" and right = "secretary" + or + wrong = "secretery" and right = "secretary" + or + wrong = "sedereal" and right = "sidereal" + or + wrong = "seeked" and right = "sought" + or + wrong = "segementation" and right = "segmentation" + or + wrong = "seguoys" and right = "segues" + or + wrong = "seige" and right = "siege" + or + wrong = "seing" and right = "seeing" + or + wrong = "seinor" and right = "senior" + or + wrong = "seldomly" and right = "seldom" + or + wrong = "senarios" and right = "scenarios" + or + wrong = "sence" and right = "sense" + or + wrong = "sence" and right = "since" + or + wrong = "senstive" and right = "sensitive" + or + wrong = "sensure" and right = "censure" + or + wrong = "seperate" and right = "separate" + or + wrong = "seperated" and right = "separated" + or + wrong = "seperately" and right = "separately" + or + wrong = "seperates" and right = "separates" + or + wrong = "seperating" and right = "separating" + or + wrong = "seperation" and right = "separation" + or + wrong = "seperatism" and right = "separatism" + or + wrong = "seperatist" and right = "separatist" + or + wrong = "seperator" and right = "separator" + or + wrong = "sepina" and right = "subpoena" + or + wrong = "sepulchure" and right = "sepulcher" + or + wrong = "sepulchure" and right = "sepulchre" + or + wrong = "sepulcre" and right = "sepulcher" + or + wrong = "sepulcre" and right = "sepulchre" + or + wrong = "sergent" and right = "sergeant" + or + wrong = "settelement" and right = "settlement" + or + wrong = "settlment" and right = "settlement" + or + wrong = "settting" and right = "setting" + or + wrong = "severeal" and right = "several" + or + wrong = "severley" and right = "severely" + or + wrong = "severly" and right = "severely" + or + wrong = "sevice" and right = "service" + or + wrong = "shadasloo" and right = "shadaloo" + or + wrong = "shaddow" and right = "shadow" + or + wrong = "shadoloo" and right = "shadaloo" + or + wrong = "shamen" and right = "shaman" + or + wrong = "shamen" and right = "shamans" + or + wrong = "sheat" and right = "cheat" + or + wrong = "sheat" and right = "sheath" + or + wrong = "sheat" and right = "sheet" + or + wrong = "sheild" and right = "shield" + or + wrong = "sherif" and right = "sheriff" + or + wrong = "shineing" and right = "shining" + or + wrong = "shiped" and right = "shipped" + or + wrong = "shiping" and right = "shipping" + or + wrong = "shopkeeepers" and right = "shopkeepers" + or + wrong = "shorly" and right = "shortly" + or + wrong = "shoudl" and right = "should" + or + wrong = "shoudln" and right = "should" + or + wrong = "shreak" and right = "shriek" + or + wrong = "shrinked" and right = "shrunk" + or + wrong = "sicne" and right = "since" + or + wrong = "sideral" and right = "sidereal" + or + wrong = "sieze" and right = "seize" + or + wrong = "sieze" and right = "size" + or + wrong = "siezed" and right = "seized" + or + wrong = "siezed" and right = "sized" + or + wrong = "siezing" and right = "seizing" + or + wrong = "siezing" and right = "sizing" + or + wrong = "siezure" and right = "seizure" + or + wrong = "siezures" and right = "seizures" + or + wrong = "siginificant" and right = "significant" + or + wrong = "signficant" and right = "significant" + or + wrong = "signficiant" and right = "significant" + or + wrong = "signfies" and right = "signifies" + or + wrong = "signifantly" and right = "significantly" + or + wrong = "significently" and right = "significantly" + or + wrong = "signifigant" and right = "significant" + or + wrong = "signifigantly" and right = "significantly" + or + wrong = "signitories" and right = "signatories" + or + wrong = "signitory" and right = "signatory" + or + wrong = "similarily" and right = "similarly" + or + wrong = "similiar" and right = "similar" + or + wrong = "similiarity" and right = "similarity" + or + wrong = "similiarly" and right = "similarly" + or + wrong = "simmilar" and right = "similar" + or + wrong = "simpley" and right = "simply" + or + wrong = "simplier" and right = "simpler" + or + wrong = "simultanous" and right = "simultaneous" + or + wrong = "simultanously" and right = "simultaneously" + or + wrong = "sincerley" and right = "sincerely" + or + wrong = "singsog" and right = "singsong" + or + wrong = "sinse" and right = "since" + or + wrong = "sinse" and right = "sines" + or + wrong = "sionist" and right = "zionist" + or + wrong = "sionists" and right = "zionists" + or + wrong = "sixtin" and right = "sistine" + or + wrong = "skagerak" and right = "skagerrak" + or + wrong = "skateing" and right = "skating" + or + wrong = "slaugterhouses" and right = "slaughterhouses" + or + wrong = "slighly" and right = "slightly" + or + wrong = "slippy" and right = "slippery" + or + wrong = "slowy" and right = "slowly" + or + wrong = "smae" and right = "same" + or + wrong = "smealting" and right = "smelting" + or + wrong = "smoe" and right = "some" + or + wrong = "sneeks" and right = "sneaks" + or + wrong = "snese" and right = "sneeze" + or + wrong = "socalism" and right = "socialism" + or + wrong = "socities" and right = "societies" + or + wrong = "soem" and right = "some" + or + wrong = "sofware" and right = "software" + or + wrong = "sohw" and right = "show" + or + wrong = "soilders" and right = "soldiers" + or + wrong = "solatary" and right = "solitary" + or + wrong = "soley" and right = "solely" + or + wrong = "soliders" and right = "soldiers" + or + wrong = "soliliquy" and right = "soliloquy" + or + wrong = "soluable" and right = "soluble" + or + wrong = "somene" and right = "someone" + or + wrong = "somtimes" and right = "sometimes" + or + wrong = "somwhere" and right = "somewhere" + or + wrong = "sophicated" and right = "sophisticated" + or + wrong = "sophmore" and right = "sophomore" + or + wrong = "sorceror" and right = "sorcerer" + or + wrong = "sorrounding" and right = "surrounding" + or + wrong = "sotry" and right = "story" + or + wrong = "sotyr" and right = "satyr" + or + wrong = "sotyr" and right = "story" + or + wrong = "soudn" and right = "sound" + or + wrong = "soudns" and right = "sounds" + or + wrong = "sould" and right = "could" + or + wrong = "sould" and right = "should" + or + wrong = "sould" and right = "sold" + or + wrong = "sould" and right = "soul" + or + wrong = "sountrack" and right = "soundtrack" + or + wrong = "sourth" and right = "south" + or + wrong = "sourthern" and right = "southern" + or + wrong = "souvenier" and right = "souvenir" + or + wrong = "souveniers" and right = "souvenirs" + or + wrong = "soveits" and right = "soviets" + or + wrong = "sovereignity" and right = "sovereignty" + or + wrong = "soverign" and right = "sovereign" + or + wrong = "soverignity" and right = "sovereignty" + or + wrong = "soverignty" and right = "sovereignty" + or + wrong = "spainish" and right = "spanish" + or + wrong = "speach" and right = "speech" + or + wrong = "specfic" and right = "specific" + or + wrong = "speciallized" and right = "specialised" + or + wrong = "speciallized" and right = "specialized" + or + wrong = "specif" and right = "specific" + or + wrong = "specif" and right = "specify" + or + wrong = "specifiying" and right = "specifying" + or + wrong = "speciman" and right = "specimen" + or + wrong = "spectauclar" and right = "spectacular" + or + wrong = "spectaulars" and right = "spectaculars" + or + wrong = "spects" and right = "aspects" + or + wrong = "spects" and right = "expects" + or + wrong = "spectum" and right = "spectrum" + or + wrong = "speices" and right = "species" + or + wrong = "spendour" and right = "splendour" + or + wrong = "spermatozoan" and right = "spermatozoon" + or + wrong = "spoace" and right = "space" + or + wrong = "sponser" and right = "sponsor" + or + wrong = "sponsered" and right = "sponsored" + or + wrong = "spontanous" and right = "spontaneous" + or + wrong = "sponzored" and right = "sponsored" + or + wrong = "spoonfulls" and right = "spoonfuls" + or + wrong = "sppeches" and right = "speeches" + or + wrong = "spreaded" and right = "spread" + or + wrong = "sprech" and right = "speech" + or + wrong = "spred" and right = "spread" + or + wrong = "spriritual" and right = "spiritual" + or + wrong = "spritual" and right = "spiritual" + or + wrong = "sqaure" and right = "square" + or + wrong = "sring" and right = "string" + or + wrong = "stablility" and right = "stability" + or + wrong = "stainlees" and right = "stainless" + or + wrong = "staion" and right = "station" + or + wrong = "standars" and right = "standards" + or + wrong = "stange" and right = "strange" + or + wrong = "startegic" and right = "strategic" + or + wrong = "startegies" and right = "strategies" + or + wrong = "startegy" and right = "strategy" + or + wrong = "stateman" and right = "statesman" + or + wrong = "statememts" and right = "statements" + or + wrong = "statment" and right = "statement" + or + wrong = "steriods" and right = "steroids" + or + wrong = "sterotypes" and right = "stereotypes" + or + wrong = "stilus" and right = "stylus" + or + wrong = "stingent" and right = "stringent" + or + wrong = "stiring" and right = "stirring" + or + wrong = "stirrs" and right = "stirs" + or + wrong = "stlye" and right = "style" + or + wrong = "stomache" and right = "stomach" + or + wrong = "stong" and right = "strong" + or + wrong = "stopry" and right = "story" + or + wrong = "storeis" and right = "stories" + or + wrong = "storise" and right = "stories" + or + wrong = "stornegst" and right = "strongest" + or + wrong = "stoyr" and right = "story" + or + wrong = "stpo" and right = "stop" + or + wrong = "stradegies" and right = "strategies" + or + wrong = "stradegy" and right = "strategy" + or + wrong = "strat" and right = "start" + or + wrong = "strat" and right = "strata" + or + wrong = "stratagically" and right = "strategically" + or + wrong = "streemlining" and right = "streamlining" + or + wrong = "stregth" and right = "strength" + or + wrong = "strenghen" and right = "strengthen" + or + wrong = "strenghened" and right = "strengthened" + or + wrong = "strenghening" and right = "strengthening" + or + wrong = "strenght" and right = "strength" + or + wrong = "strenghten" and right = "strengthen" + or + wrong = "strenghtened" and right = "strengthened" + or + wrong = "strenghtening" and right = "strengthening" + or + wrong = "strengtened" and right = "strengthened" + or + wrong = "strenous" and right = "strenuous" + or + wrong = "strictist" and right = "strictest" + or + wrong = "strikely" and right = "strikingly" + or + wrong = "strnad" and right = "strand" + or + wrong = "stroy" and right = "destroy" + or + wrong = "stroy" and right = "story" + or + wrong = "structual" and right = "structural" + or + wrong = "stubborness" and right = "stubbornness" + or + wrong = "stucture" and right = "structure" + or + wrong = "stuctured" and right = "structured" + or + wrong = "studdy" and right = "study" + or + wrong = "studing" and right = "studying" + or + wrong = "stuggling" and right = "struggling" + or + wrong = "sturcture" and right = "structure" + or + wrong = "subcatagories" and right = "subcategories" + or + wrong = "subcatagory" and right = "subcategory" + or + wrong = "subconsiously" and right = "subconsciously" + or + wrong = "subjudgation" and right = "subjugation" + or + wrong = "submachne" and right = "submachine" + or + wrong = "subpecies" and right = "subspecies" + or + wrong = "subsidary" and right = "subsidiary" + or + wrong = "subsiduary" and right = "subsidiary" + or + wrong = "subsquent" and right = "subsequent" + or + wrong = "subsquently" and right = "subsequently" + or + wrong = "substace" and right = "substance" + or + wrong = "substancial" and right = "substantial" + or + wrong = "substatial" and right = "substantial" + or + wrong = "substituded" and right = "substituted" + or + wrong = "substract" and right = "subtract" + or + wrong = "substracted" and right = "subtracted" + or + wrong = "substracting" and right = "subtracting" + or + wrong = "substraction" and right = "subtraction" + or + wrong = "substracts" and right = "subtracts" + or + wrong = "subtances" and right = "substances" + or + wrong = "subterranian" and right = "subterranean" + or + wrong = "suburburban" and right = "suburban" + or + wrong = "succceeded" and right = "succeeded" + or + wrong = "succcesses" and right = "successes" + or + wrong = "succedded" and right = "succeeded" + or + wrong = "succeded" and right = "succeeded" + or + wrong = "succeds" and right = "succeeds" + or + wrong = "succesful" and right = "successful" + or + wrong = "succesfully" and right = "successfully" + or + wrong = "succesfuly" and right = "successfully" + or + wrong = "succesion" and right = "succession" + or + wrong = "succesive" and right = "successive" + or + wrong = "successfull" and right = "successful" + or + wrong = "successully" and right = "successfully" + or + wrong = "succsess" and right = "success" + or + wrong = "succsessfull" and right = "successful" + or + wrong = "suceed" and right = "succeed" + or + wrong = "suceeded" and right = "succeeded" + or + wrong = "suceeding" and right = "succeeding" + or + wrong = "suceeds" and right = "succeeds" + or + wrong = "sucesful" and right = "successful" + or + wrong = "sucesfully" and right = "successfully" + or + wrong = "sucesfuly" and right = "successfully" + or + wrong = "sucesion" and right = "succession" + or + wrong = "sucess" and right = "success" + or + wrong = "sucesses" and right = "successes" + or + wrong = "sucessful" and right = "successful" + or + wrong = "sucessfull" and right = "successful" + or + wrong = "sucessfully" and right = "successfully" + or + wrong = "sucessfuly" and right = "successfully" + or + wrong = "sucession" and right = "succession" + or + wrong = "sucessive" and right = "successive" + or + wrong = "sucessor" and right = "successor" + or + wrong = "sucessot" and right = "successor" + or + wrong = "sucide" and right = "suicide" + or + wrong = "sucidial" and right = "suicidal" + or + wrong = "sudent" and right = "student" + or + wrong = "sudents" and right = "students" + or + wrong = "sufferage" and right = "suffrage" + or + wrong = "sufferred" and right = "suffered" + or + wrong = "sufferring" and right = "suffering" + or + wrong = "sufficent" and right = "sufficient" + or + wrong = "sufficently" and right = "sufficiently" + or + wrong = "sumary" and right = "summary" + or + wrong = "sunglases" and right = "sunglasses" + or + wrong = "suop" and right = "soup" + or + wrong = "superceeded" and right = "superseded" + or + wrong = "superintendant" and right = "superintendent" + or + wrong = "suphisticated" and right = "sophisticated" + or + wrong = "suplimented" and right = "supplemented" + or + wrong = "suported" and right = "supported" + or + wrong = "supose" and right = "suppose" + or + wrong = "suposed" and right = "supposed" + or + wrong = "suposedly" and right = "supposedly" + or + wrong = "suposes" and right = "supposes" + or + wrong = "suposing" and right = "supposing" + or + wrong = "supplamented" and right = "supplemented" + or + wrong = "suppliementing" and right = "supplementing" + or + wrong = "suppoed" and right = "supposed" + or + wrong = "supposingly" and right = "supposedly" + or + wrong = "suppy" and right = "supply" + or + wrong = "suprassing" and right = "surpassing" + or + wrong = "supress" and right = "suppress" + or + wrong = "supressed" and right = "suppressed" + or + wrong = "supresses" and right = "suppresses" + or + wrong = "supressing" and right = "suppressing" + or + wrong = "suprise" and right = "surprise" + or + wrong = "suprised" and right = "surprised" + or + wrong = "suprising" and right = "surprising" + or + wrong = "suprisingly" and right = "surprisingly" + or + wrong = "suprize" and right = "surprise" + or + wrong = "suprized" and right = "surprised" + or + wrong = "suprizing" and right = "surprising" + or + wrong = "suprizingly" and right = "surprisingly" + or + wrong = "surfce" and right = "surface" + or + wrong = "surley" and right = "surely" + or + wrong = "surley" and right = "surly" + or + wrong = "suround" and right = "surround" + or + wrong = "surounded" and right = "surrounded" + or + wrong = "surounding" and right = "surrounding" + or + wrong = "suroundings" and right = "surroundings" + or + wrong = "surounds" and right = "surrounds" + or + wrong = "surplanted" and right = "supplanted" + or + wrong = "surpress" and right = "suppress" + or + wrong = "surpressed" and right = "suppressed" + or + wrong = "surprize" and right = "surprise" + or + wrong = "surprized" and right = "surprised" + or + wrong = "surprizing" and right = "surprising" + or + wrong = "surprizingly" and right = "surprisingly" + or + wrong = "surrended" and right = "surrendered" + or + wrong = "surrended" and right = "surrounded" + or + wrong = "surrepetitious" and right = "surreptitious" + or + wrong = "surrepetitiously" and right = "surreptitiously" + or + wrong = "surreptious" and right = "surreptitious" + or + wrong = "surreptiously" and right = "surreptitiously" + or + wrong = "surronded" and right = "surrounded" + or + wrong = "surrouded" and right = "surrounded" + or + wrong = "surrouding" and right = "surrounding" + or + wrong = "surrundering" and right = "surrendering" + or + wrong = "surveilence" and right = "surveillance" + or + wrong = "surveill" and right = "surveil" + or + wrong = "surveyer" and right = "surveyor" + or + wrong = "surviver" and right = "survivor" + or + wrong = "survivers" and right = "survivors" + or + wrong = "survivied" and right = "survived" + or + wrong = "suseptable" and right = "susceptible" + or + wrong = "suseptible" and right = "susceptible" + or + wrong = "suspention" and right = "suspension" + or + wrong = "swaer" and right = "swear" + or + wrong = "swaers" and right = "swears" + or + wrong = "swepth" and right = "swept" + or + wrong = "swiming" and right = "swimming" + or + wrong = "syas" and right = "says" + or + wrong = "symetrical" and right = "symmetrical" + or + wrong = "symetrically" and right = "symmetrically" + or + wrong = "symetry" and right = "symmetry" + or + wrong = "symettric" and right = "symmetric" + or + wrong = "symmetral" and right = "symmetric" + or + wrong = "symmetricaly" and right = "symmetrically" + or + wrong = "synagouge" and right = "synagogue" + or + wrong = "syncronization" and right = "synchronization" + or + wrong = "synonomous" and right = "synonymous" + or + wrong = "synonymns" and right = "synonyms" + or + wrong = "synphony" and right = "symphony" + or + wrong = "syphyllis" and right = "syphilis" + or + wrong = "sypmtoms" and right = "symptoms" + or + wrong = "syrap" and right = "syrup" + or + wrong = "sysmatically" and right = "systematically" + or + wrong = "sytem" and right = "system" + or + wrong = "sytle" and right = "style" + or + wrong = "tabacco" and right = "tobacco" + or + wrong = "tahn" and right = "than" + or + wrong = "taht" and right = "that" + or + wrong = "talekd" and right = "talked" + or + wrong = "targetted" and right = "targeted" + or + wrong = "targetting" and right = "targeting" + or + wrong = "tast" and right = "taste" + or + wrong = "tath" and right = "that" + or + wrong = "tatoo" and right = "tattoo" + or + wrong = "tattooes" and right = "tattoos" + or + wrong = "taxanomic" and right = "taxonomic" + or + wrong = "taxanomy" and right = "taxonomy" + or + wrong = "teached" and right = "taught" + or + wrong = "techician" and right = "technician" + or + wrong = "techicians" and right = "technicians" + or + wrong = "techiniques" and right = "techniques" + or + wrong = "technitian" and right = "technician" + or + wrong = "technnology" and right = "technology" + or + wrong = "technolgy" and right = "technology" + or + wrong = "teh" and right = "the" + or + wrong = "tehy" and right = "they" + or + wrong = "telelevision" and right = "television" + or + wrong = "televsion" and right = "television" + or + wrong = "telphony" and right = "telephony" + or + wrong = "temerature" and right = "temperature" + or + wrong = "tempalte" and right = "template" + or + wrong = "tempaltes" and right = "templates" + or + wrong = "temparate" and right = "temperate" + or + wrong = "temperarily" and right = "temporarily" + or + wrong = "temperment" and right = "temperament" + or + wrong = "tempertaure" and right = "temperature" + or + wrong = "temperture" and right = "temperature" + or + wrong = "temprary" and right = "temporary" + or + wrong = "tenacle" and right = "tentacle" + or + wrong = "tenacles" and right = "tentacles" + or + wrong = "tendacy" and right = "tendency" + or + wrong = "tendancies" and right = "tendencies" + or + wrong = "tendancy" and right = "tendency" + or + wrong = "tepmorarily" and right = "temporarily" + or + wrong = "terrestial" and right = "terrestrial" + or + wrong = "terriories" and right = "territories" + or + wrong = "terriory" and right = "territory" + or + wrong = "territorist" and right = "terrorist" + or + wrong = "territoy" and right = "territory" + or + wrong = "terroist" and right = "terrorist" + or + wrong = "testiclular" and right = "testicular" + or + wrong = "testomony" and right = "testimony" + or + wrong = "tghe" and right = "the" + or + wrong = "thast" and right = "that" + or + wrong = "theather" and right = "theater" + or + wrong = "theese" and right = "these" + or + wrong = "theif" and right = "thief" + or + wrong = "theives" and right = "thieves" + or + wrong = "themselfs" and right = "themselves" + or + wrong = "themslves" and right = "themselves" + or + wrong = "ther" and right = "the" + or + wrong = "ther" and right = "their" + or + wrong = "ther" and right = "there" + or + wrong = "therafter" and right = "thereafter" + or + wrong = "therby" and right = "thereby" + or + wrong = "theri" and right = "their" + or + wrong = "thgat" and right = "that" + or + wrong = "thge" and right = "the" + or + wrong = "thier" and right = "their" + or + wrong = "thign" and right = "thing" + or + wrong = "thigns" and right = "things" + or + wrong = "thigsn" and right = "things" + or + wrong = "thikn" and right = "think" + or + wrong = "thikning" and right = "thickening" + or + wrong = "thikning" and right = "thinking" + or + wrong = "thikns" and right = "thinks" + or + wrong = "thiunk" and right = "think" + or + wrong = "thn" and right = "then" + or + wrong = "thna" and right = "than" + or + wrong = "thne" and right = "then" + or + wrong = "thnig" and right = "thing" + or + wrong = "thnigs" and right = "things" + or + wrong = "thoughout" and right = "throughout" + or + wrong = "threatend" and right = "threatened" + or + wrong = "threatning" and right = "threatening" + or + wrong = "threee" and right = "three" + or + wrong = "threshhold" and right = "threshold" + or + wrong = "thrid" and right = "third" + or + wrong = "throrough" and right = "thorough" + or + wrong = "throughly" and right = "thoroughly" + or + wrong = "throught" and right = "thought" + or + wrong = "throught" and right = "through" + or + wrong = "throught" and right = "throughout" + or + wrong = "througout" and right = "throughout" + or + wrong = "thru" and right = "through" + or + wrong = "thsi" and right = "this" + or + wrong = "thsoe" and right = "those" + or + wrong = "thta" and right = "that" + or + wrong = "thyat" and right = "that" + or + wrong = "tiem" and right = "tim" + or + wrong = "tiem" and right = "time" + or + wrong = "tihkn" and right = "think" + or + wrong = "tihs" and right = "this" + or + wrong = "timne" and right = "time" + or + wrong = "tiome" and right = "time" + or + wrong = "tiome" and right = "tome" + or + wrong = "tje" and right = "the" + or + wrong = "tjhe" and right = "the" + or + wrong = "tjpanishad" and right = "upanishad" + or + wrong = "tkae" and right = "take" + or + wrong = "tkaes" and right = "takes" + or + wrong = "tkaing" and right = "taking" + or + wrong = "tlaking" and right = "talking" + or + wrong = "tobbaco" and right = "tobacco" + or + wrong = "todya" and right = "today" + or + wrong = "toghether" and right = "together" + or + wrong = "toke" and right = "took" + or + wrong = "tolerence" and right = "tolerance" + or + wrong = "tolkein" and right = "tolkien" + or + wrong = "tomatos" and right = "tomatoes" + or + wrong = "tommorow" and right = "tomorrow" + or + wrong = "tommorrow" and right = "tomorrow" + or + wrong = "tongiht" and right = "tonight" + or + wrong = "toriodal" and right = "toroidal" + or + wrong = "tormenters" and right = "tormentors" + or + wrong = "tornadoe" and right = "tornado" + or + wrong = "torpeados" and right = "torpedoes" + or + wrong = "torpedos" and right = "torpedoes" + or + wrong = "tortise" and right = "tortoise" + or + wrong = "toubles" and right = "troubles" + or + wrong = "tounge" and right = "tongue" + or + wrong = "tourch" and right = "torch" + or + wrong = "tourch" and right = "touch" + or + wrong = "towords" and right = "towards" + or + wrong = "towrad" and right = "toward" + or + wrong = "tradionally" and right = "traditionally" + or + wrong = "traditionaly" and right = "traditionally" + or + wrong = "traditionnal" and right = "traditional" + or + wrong = "traditition" and right = "tradition" + or + wrong = "tradtionally" and right = "traditionally" + or + wrong = "trafficed" and right = "trafficked" + or + wrong = "trafficing" and right = "trafficking" + or + wrong = "trafic" and right = "traffic" + or + wrong = "trancendent" and right = "transcendent" + or + wrong = "trancending" and right = "transcending" + or + wrong = "tranform" and right = "transform" + or + wrong = "tranformed" and right = "transformed" + or + wrong = "transcendance" and right = "transcendence" + or + wrong = "transcendant" and right = "transcendent" + or + wrong = "transcendentational" and right = "transcendental" + or + wrong = "transcripting" and right = "transcribing" + or + wrong = "transcripting" and right = "transcription" + or + wrong = "transending" and right = "transcending" + or + wrong = "transesxuals" and right = "transsexuals" + or + wrong = "transfered" and right = "transferred" + or + wrong = "transfering" and right = "transferring" + or + wrong = "transformaton" and right = "transformation" + or + wrong = "transistion" and right = "transition" + or + wrong = "translater" and right = "translator" + or + wrong = "translaters" and right = "translators" + or + wrong = "transmissable" and right = "transmissible" + or + wrong = "transporation" and right = "transportation" + or + wrong = "tremelo" and right = "tremolo" + or + wrong = "tremelos" and right = "tremolos" + or + wrong = "treshold" and right = "threshold" + or + wrong = "triguered" and right = "triggered" + or + wrong = "triology" and right = "trilogy" + or + wrong = "troling" and right = "trolling" + or + wrong = "troup" and right = "troupe" + or + wrong = "troups" and right = "troops" + or + wrong = "troups" and right = "troupes" + or + wrong = "truely" and right = "truly" + or + wrong = "trustworthyness" and right = "trustworthiness" + or + wrong = "turnk" and right = "trunk" + or + wrong = "turnk" and right = "turnkey" + or + wrong = "tuscon" and right = "tucson" + or + wrong = "tust" and right = "trust" + or + wrong = "twelth" and right = "twelfth" + or + wrong = "twon" and right = "town" + or + wrong = "twpo" and right = "two" + or + wrong = "tyhat" and right = "that" + or + wrong = "tyhe" and right = "they" + or + wrong = "typcial" and right = "typical" + or + wrong = "typicaly" and right = "typically" + or + wrong = "tyranies" and right = "tyrannies" + or + wrong = "tyrany" and right = "tyranny" + or + wrong = "tyrranies" and right = "tyrannies" + or + wrong = "tyrrany" and right = "tyranny" + or + wrong = "ubiquitious" and right = "ubiquitous" + or + wrong = "ublisher" and right = "publisher" + or + wrong = "udpate" and right = "update" + or + wrong = "uise" and right = "use" + or + wrong = "ukranian" and right = "ukrainian" + or + wrong = "ultimely" and right = "ultimately" + or + wrong = "unacompanied" and right = "unaccompanied" + or + wrong = "unahppy" and right = "unhappy" + or + wrong = "unanymous" and right = "unanimous" + or + wrong = "unathorised" and right = "unauthorised" + or + wrong = "unavailible" and right = "unavailable" + or + wrong = "unballance" and right = "unbalance" + or + wrong = "unbeknowst" and right = "unbeknownst" + or + wrong = "unbeleivable" and right = "unbelievable" + or + wrong = "uncertainity" and right = "uncertainty" + or + wrong = "unchallengable" and right = "unchallengeable" + or + wrong = "unchangable" and right = "unchangeable" + or + wrong = "uncompetive" and right = "uncompetitive" + or + wrong = "unconcious" and right = "unconscious" + or + wrong = "unconciousness" and right = "unconsciousness" + or + wrong = "unconfortability" and right = "discomfort" + or + wrong = "uncontitutional" and right = "unconstitutional" + or + wrong = "unconvential" and right = "unconventional" + or + wrong = "undecideable" and right = "undecidable" + or + wrong = "understoon" and right = "understood" + or + wrong = "undesireable" and right = "undesirable" + or + wrong = "undetecable" and right = "undetectable" + or + wrong = "undoubtely" and right = "undoubtedly" + or + wrong = "undreground" and right = "underground" + or + wrong = "uneccesary" and right = "unnecessary" + or + wrong = "unecessary" and right = "unnecessary" + or + wrong = "unequalities" and right = "inequalities" + or + wrong = "unforetunately" and right = "unfortunately" + or + wrong = "unforgetable" and right = "unforgettable" + or + wrong = "unforgiveable" and right = "unforgivable" + or + wrong = "unforseen" and right = "unforeseen" + or + wrong = "unfortunatley" and right = "unfortunately" + or + wrong = "unfortunatly" and right = "unfortunately" + or + wrong = "unfourtunately" and right = "unfortunately" + or + wrong = "unihabited" and right = "uninhabited" + or + wrong = "unilateraly" and right = "unilaterally" + or + wrong = "unilatreal" and right = "unilateral" + or + wrong = "unilatreally" and right = "unilaterally" + or + wrong = "uninterruped" and right = "uninterrupted" + or + wrong = "uninterupted" and right = "uninterrupted" + or + wrong = "unintialized" and right = "uninitialized" + or + wrong = "unitesstates" and right = "unitedstates" + or + wrong = "univeral" and right = "universal" + or + wrong = "univeristies" and right = "universities" + or + wrong = "univeristy" and right = "university" + or + wrong = "univerity" and right = "university" + or + wrong = "universtiy" and right = "university" + or + wrong = "univesities" and right = "universities" + or + wrong = "univesity" and right = "university" + or + wrong = "unkown" and right = "unknown" + or + wrong = "unlikey" and right = "unlikely" + or + wrong = "unmanouverable" and right = "unmaneuverable" + or + wrong = "unmanouverable" and right = "unmanoeuvrable" + or + wrong = "unmistakeably" and right = "unmistakably" + or + wrong = "unneccesarily" and right = "unnecessarily" + or + wrong = "unneccesary" and right = "unnecessary" + or + wrong = "unneccessarily" and right = "unnecessarily" + or + wrong = "unneccessary" and right = "unnecessary" + or + wrong = "unnecesarily" and right = "unnecessarily" + or + wrong = "unnecesary" and right = "unnecessary" + or + wrong = "unoffical" and right = "unofficial" + or + wrong = "unoperational" and right = "nonoperational" + or + wrong = "unoticeable" and right = "unnoticeable" + or + wrong = "unplease" and right = "displease" + or + wrong = "unplesant" and right = "unpleasant" + or + wrong = "unprecendented" and right = "unprecedented" + or + wrong = "unprecidented" and right = "unprecedented" + or + wrong = "unrepentent" and right = "unrepentant" + or + wrong = "unrepetant" and right = "unrepentant" + or + wrong = "unrepetent" and right = "unrepentant" + or + wrong = "unsed" and right = "unsaid" + or + wrong = "unsed" and right = "unused" + or + wrong = "unsed" and right = "used" + or + wrong = "unsubstanciated" and right = "unsubstantiated" + or + wrong = "unsuccesful" and right = "unsuccessful" + or + wrong = "unsuccesfully" and right = "unsuccessfully" + or + wrong = "unsuccessfull" and right = "unsuccessful" + or + wrong = "unsucesful" and right = "unsuccessful" + or + wrong = "unsucesfuly" and right = "unsuccessfully" + or + wrong = "unsucessful" and right = "unsuccessful" + or + wrong = "unsucessfull" and right = "unsuccessful" + or + wrong = "unsucessfully" and right = "unsuccessfully" + or + wrong = "unsuprised" and right = "unsurprised" + or + wrong = "unsuprising" and right = "unsurprising" + or + wrong = "unsuprisingly" and right = "unsurprisingly" + or + wrong = "unsuprized" and right = "unsurprised" + or + wrong = "unsuprizing" and right = "unsurprising" + or + wrong = "unsuprizingly" and right = "unsurprisingly" + or + wrong = "unsurprized" and right = "unsurprised" + or + wrong = "unsurprizing" and right = "unsurprising" + or + wrong = "unsurprizingly" and right = "unsurprisingly" + or + wrong = "untill" and right = "until" + or + wrong = "untranslateable" and right = "untranslatable" + or + wrong = "unuseable" and right = "unusable" + or + wrong = "unusuable" and right = "unusable" + or + wrong = "unviersity" and right = "university" + or + wrong = "unwarrented" and right = "unwarranted" + or + wrong = "unweildly" and right = "unwieldy" + or + wrong = "unwieldly" and right = "unwieldy" + or + wrong = "upcomming" and right = "upcoming" + or + wrong = "upgradded" and right = "upgraded" + or + wrong = "usally" and right = "usually" + or + wrong = "useage" and right = "usage" + or + wrong = "usefull" and right = "useful" + or + wrong = "usefuly" and right = "usefully" + or + wrong = "useing" and right = "using" + or + wrong = "usualy" and right = "usually" + or + wrong = "ususally" and right = "usually" + or + wrong = "vaccum" and right = "vacuum" + or + wrong = "vaccume" and right = "vacuum" + or + wrong = "vacinity" and right = "vicinity" + or + wrong = "vaguaries" and right = "vagaries" + or + wrong = "vaieties" and right = "varieties" + or + wrong = "vailidty" and right = "validity" + or + wrong = "valetta" and right = "valletta" + or + wrong = "valuble" and right = "valuable" + or + wrong = "valueable" and right = "valuable" + or + wrong = "varations" and right = "variations" + or + wrong = "varient" and right = "variant" + or + wrong = "variey" and right = "variety" + or + wrong = "varing" and right = "varying" + or + wrong = "varities" and right = "varieties" + or + wrong = "varity" and right = "variety" + or + wrong = "vasall" and right = "vassal" + or + wrong = "vasalls" and right = "vassals" + or + wrong = "vaule" and right = "value" + or + wrong = "vegatarian" and right = "vegetarian" + or + wrong = "vegitable" and right = "vegetable" + or + wrong = "vegitables" and right = "vegetables" + or + wrong = "vegtable" and right = "vegetable" + or + wrong = "vehicule" and right = "vehicle" + or + wrong = "vell" and right = "well" + or + wrong = "venemous" and right = "venomous" + or + wrong = "vengance" and right = "vengeance" + or + wrong = "vengence" and right = "vengeance" + or + wrong = "verfication" and right = "verification" + or + wrong = "verison" and right = "version" + or + wrong = "verisons" and right = "versions" + or + wrong = "vermillion" and right = "vermilion" + or + wrong = "versitilaty" and right = "versatility" + or + wrong = "versitlity" and right = "versatility" + or + wrong = "vetween" and right = "between" + or + wrong = "veyr" and right = "very" + or + wrong = "vigeur" and right = "vigor" + or + wrong = "vigeur" and right = "vigour" + or + wrong = "vigeur" and right = "vigueur" + or + wrong = "vigilence" and right = "vigilance" + or + wrong = "vigourous" and right = "vigorous" + or + wrong = "villian" and right = "villain" + or + wrong = "villification" and right = "vilification" + or + wrong = "villify" and right = "vilify" + or + wrong = "villin" and right = "villain" + or + wrong = "villin" and right = "villein" + or + wrong = "villin" and right = "villi" + or + wrong = "vincinity" and right = "vicinity" + or + wrong = "violentce" and right = "violence" + or + wrong = "virtualy" and right = "virtually" + or + wrong = "virutal" and right = "virtual" + or + wrong = "virutally" and right = "virtually" + or + wrong = "visable" and right = "visible" + or + wrong = "visably" and right = "visibly" + or + wrong = "visting" and right = "visiting" + or + wrong = "vistors" and right = "visitors" + or + wrong = "vitories" and right = "victories" + or + wrong = "volcanoe" and right = "volcano" + or + wrong = "voleyball" and right = "volleyball" + or + wrong = "volontary" and right = "voluntary" + or + wrong = "volonteer" and right = "volunteer" + or + wrong = "volonteered" and right = "volunteered" + or + wrong = "volonteering" and right = "volunteering" + or + wrong = "volonteers" and right = "volunteers" + or + wrong = "volounteer" and right = "volunteer" + or + wrong = "volounteered" and right = "volunteered" + or + wrong = "volounteering" and right = "volunteering" + or + wrong = "volounteers" and right = "volunteers" + or + wrong = "volumne" and right = "volume" + or + wrong = "vreity" and right = "variety" + or + wrong = "vrey" and right = "very" + or + wrong = "vriety" and right = "variety" + or + wrong = "vulnerablility" and right = "vulnerability" + or + wrong = "vyer" and right = "very" + or + wrong = "vyre" and right = "very" + or + wrong = "waht" and right = "what" + or + wrong = "warantee" and right = "warranty" + or + wrong = "wardobe" and right = "wardrobe" + or + wrong = "warrent" and right = "warrant" + or + wrong = "warrriors" and right = "warriors" + or + wrong = "wass" and right = "was" + or + wrong = "watn" and right = "want" + or + wrong = "wayword" and right = "wayward" + or + wrong = "weaponary" and right = "weaponry" + or + wrong = "weas" and right = "was" + or + wrong = "wehn" and right = "when" + or + wrong = "weild" and right = "wield" + or + wrong = "weild" and right = "wild" + or + wrong = "weilded" and right = "wielded" + or + wrong = "wendsay" and right = "wednesday" + or + wrong = "wensday" and right = "wednesday" + or + wrong = "wereabouts" and right = "whereabouts" + or + wrong = "whant" and right = "want" + or + wrong = "whants" and right = "wants" + or + wrong = "whcih" and right = "which" + or + wrong = "wheras" and right = "whereas" + or + wrong = "wherease" and right = "whereas" + or + wrong = "whereever" and right = "wherever" + or + wrong = "whic" and right = "which" + or + wrong = "whihc" and right = "which" + or + wrong = "whith" and right = "with" + or + wrong = "whlch" and right = "which" + or + wrong = "whn" and right = "when" + or + wrong = "wholey" and right = "wholly" + or + wrong = "wholy" and right = "holy" + or + wrong = "wholy" and right = "wholly" + or + wrong = "whta" and right = "what" + or + wrong = "whther" and right = "whether" + or + wrong = "wich" and right = "which" + or + wrong = "wich" and right = "witch" + or + wrong = "widesread" and right = "widespread" + or + wrong = "wief" and right = "wife" + or + wrong = "wierd" and right = "weird" + or + wrong = "wiew" and right = "view" + or + wrong = "wih" and right = "with" + or + wrong = "wiht" and right = "with" + or + wrong = "wille" and right = "will" + or + wrong = "willingless" and right = "willingness" + or + wrong = "willk" and right = "will" + or + wrong = "wirting" and right = "writing" + or + wrong = "withdrawl" and right = "withdraw" + or + wrong = "withdrawl" and right = "withdrawal" + or + wrong = "witheld" and right = "withheld" + or + wrong = "withh" and right = "with" + or + wrong = "withing" and right = "within" + or + wrong = "withold" and right = "withhold" + or + wrong = "witht" and right = "with" + or + wrong = "witn" and right = "with" + or + wrong = "wiull" and right = "will" + or + wrong = "wnat" and right = "want" + or + wrong = "wnated" and right = "wanted" + or + wrong = "wnats" and right = "wants" + or + wrong = "wohle" and right = "whole" + or + wrong = "wokr" and right = "work" + or + wrong = "wokring" and right = "working" + or + wrong = "wonderfull" and right = "wonderful" + or + wrong = "wordlwide" and right = "worldwide" + or + wrong = "workststion" and right = "workstation" + or + wrong = "worls" and right = "world" + or + wrong = "worstened" and right = "worsened" + or + wrong = "woudl" and right = "would" + or + wrong = "wresters" and right = "wrestlers" + or + wrong = "wriet" and right = "write" + or + wrong = "writen" and right = "written" + or + wrong = "wroet" and right = "wrote" + or + wrong = "wrok" and right = "work" + or + wrong = "wroking" and right = "working" + or + wrong = "wtih" and right = "with" + or + wrong = "wupport" and right = "support" + or + wrong = "xenophoby" and right = "xenophobia" + or + wrong = "yaching" and right = "yachting" + or + wrong = "yaer" and right = "year" + or + wrong = "yaerly" and right = "yearly" + or + wrong = "yaers" and right = "years" + or + wrong = "yatch" and right = "yacht" + or + wrong = "yearm" and right = "year" + or + wrong = "yeasr" and right = "years" + or + wrong = "yeild" and right = "yield" + or + wrong = "yeilding" and right = "yielding" + or + wrong = "yementite" and right = "yemeni" + or + wrong = "yementite" and right = "yemenite" + or + wrong = "yera" and right = "year" + or + wrong = "yeras" and right = "years" + or + wrong = "yersa" and right = "years" + or + wrong = "yotube" and right = "youtube" + or + wrong = "youseff" and right = "yousef" + or + wrong = "youself" and right = "yourself" + or + wrong = "yrea" and right = "year" + or + wrong = "ytou" and right = "you" + or + wrong = "yuo" and right = "you" + or + wrong = "zeebra" and right = "zebra" +} diff --git a/shared/typos/qlpack.yml b/shared/typos/qlpack.yml new file mode 100644 index 00000000000..f915e6407f8 --- /dev/null +++ b/shared/typos/qlpack.yml @@ -0,0 +1,4 @@ +name: codeql/typos +version: 0.0.1-dev +groups: shared +library: true diff --git a/swift/ql/lib/codeql/swift/elements/AstNode.qll b/swift/ql/lib/codeql/swift/elements/AstNode.qll index 01f0fedb710..33b4da963b7 100644 --- a/swift/ql/lib/codeql/swift/elements/AstNode.qll +++ b/swift/ql/lib/codeql/swift/elements/AstNode.qll @@ -1,17 +1,30 @@ private import codeql.swift.generated.AstNode private import codeql.swift.elements.decl.AbstractFunctionDecl +private import codeql.swift.elements.decl.Decl private import codeql.swift.generated.ParentChild -private Element getEnclosingFunctionStep(Element e) { - not e instanceof AbstractFunctionDecl and - result = getImmediateParent(e) -} +private module Cached { + private Element getEnclosingDeclStep(Element e) { + not e instanceof Decl and + result = getImmediateParent(e) + } -cached -private AbstractFunctionDecl getEnclosingFunctionCached(AstNode ast) { - result = getEnclosingFunctionStep*(getImmediateParent(ast)) + cached + Decl getEnclosingDecl(AstNode ast) { result = getEnclosingDeclStep*(getImmediateParent(ast)) } + + private Element getEnclosingFunctionStep(Element e) { + not e instanceof AbstractFunctionDecl and + result = getEnclosingDecl(e) + } + + cached + AbstractFunctionDecl getEnclosingFunction(AstNode ast) { + result = getEnclosingFunctionStep*(getEnclosingDecl(ast)) + } } class AstNode extends AstNodeBase { - final AbstractFunctionDecl getEnclosingFunction() { result = getEnclosingFunctionCached(this) } + final AbstractFunctionDecl getEnclosingFunction() { result = Cached::getEnclosingFunction(this) } + + final Decl getEnclosingDecl() { result = Cached::getEnclosingDecl(this) } } diff --git a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll index b7f0e1e95c3..59440c27a51 100644 --- a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll +++ b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll @@ -121,20 +121,24 @@ class SensitiveExpr extends Expr { SensitiveDataType sensitiveType; SensitiveExpr() { - // variable reference - this.(DeclRefExpr).getDecl().(SensitiveVarDecl).hasInfo(label, sensitiveType) - or - // member variable reference - this.(MemberRefExpr).getMember().(SensitiveVarDecl).hasInfo(label, sensitiveType) - or - // function call - this.(ApplyExpr).getStaticTarget().(SensitiveFunctionDecl).hasInfo(label, sensitiveType) - or - // sensitive argument - exists(SensitiveArgument a | - a.hasInfo(label, sensitiveType) and - a.getExpr() = this - ) + ( + // variable reference + this.(DeclRefExpr).getDecl().(SensitiveVarDecl).hasInfo(label, sensitiveType) + or + // member variable reference + this.(MemberRefExpr).getMember().(SensitiveVarDecl).hasInfo(label, sensitiveType) + or + // function call + this.(ApplyExpr).getStaticTarget().(SensitiveFunctionDecl).hasInfo(label, sensitiveType) + or + // sensitive argument + exists(SensitiveArgument a | + a.hasInfo(label, sensitiveType) and + a.getExpr() = this + ) + ) and + // do not mark as sensitive it if it is probably safe + not label.toLowerCase().regexpMatch(regexpProbablySafe()) } /** @@ -146,13 +150,6 @@ class SensitiveExpr extends Expr { * Gets the type of sensitive expression this is. */ SensitiveDataType getSensitiveType() { result = sensitiveType } - - /** - * Holds if this sensitive expression might be safe because it contains - * hashed or encrypted data, or is only a reference to data that is stored - * elsewhere. - */ - predicate isProbablySafe() { label.toLowerCase().regexpMatch(regexpProbablySafe()) } } /** diff --git a/swift/ql/lib/qlpack.lock.yml b/swift/ql/lib/qlpack.lock.yml new file mode 100644 index 00000000000..06dd07fc7dc --- /dev/null +++ b/swift/ql/lib/qlpack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 diff --git a/swift/ql/src/qlpack.lock.yml b/swift/ql/src/qlpack.lock.yml new file mode 100644 index 00000000000..06dd07fc7dc --- /dev/null +++ b/swift/ql/src/qlpack.lock.yml @@ -0,0 +1,4 @@ +--- +dependencies: {} +compiled: false +lockVersion: 1.0.0 diff --git a/swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.ql b/swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.ql index 02d96b04f9f..ade02db56ce 100644 --- a/swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.ql +++ b/swift/ql/src/queries/Security/CWE-311/CleartextStorageDatabase.ql @@ -70,12 +70,7 @@ class RealmStore extends Stored { class CleartextStorageConfig extends TaintTracking::Configuration { CleartextStorageConfig() { this = "CleartextStorageConfig" } - override predicate isSource(DataFlow::Node node) { - exists(SensitiveExpr e | - node.asExpr() = e and - not e.isProbablySafe() - ) - } + override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr } override predicate isSink(DataFlow::Node node) { node.asExpr() instanceof Stored } diff --git a/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql b/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql index d3ce257b753..eb184fc233b 100644 --- a/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql +++ b/swift/ql/src/queries/Security/CWE-311/CleartextTransmission.ql @@ -42,8 +42,8 @@ class NWConnectionSend extends Transmitted { * An `Expr` that is used to form a `URL`. Such expressions are very likely to * be transmitted over a network, because that's what URLs are for. */ -class URL extends Transmitted { - URL() { +class Url extends Transmitted { + Url() { // `string` arg in `URL.init` is a sink // (we assume here that the URL goes on to be used in a network operation) exists(ClassDecl c, AbstractFunctionDecl f, CallExpr call | @@ -63,12 +63,7 @@ class URL extends Transmitted { class CleartextTransmissionConfig extends TaintTracking::Configuration { CleartextTransmissionConfig() { this = "CleartextTransmissionConfig" } - override predicate isSource(DataFlow::Node node) { - exists(SensitiveExpr e | - node.asExpr() = e and - not e.isProbablySafe() - ) - } + override predicate isSource(DataFlow::Node node) { node.asExpr() instanceof SensitiveExpr } override predicate isSink(DataFlow::Node node) { node.asExpr() instanceof Transmitted } diff --git a/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.qhelp b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.qhelp new file mode 100755 index 00000000000..d6806b2ddcc --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.qhelp @@ -0,0 +1,84 @@ + + + +

    + Using a broken or weak cryptographic hash function can leave data + vulnerable, and should not be used in security related code. +

    + +

    + A strong cryptographic hash function should be resistant to: +

    +
      +
    • + Pre-image attacks. If you know a hash value h(x), + you should not be able to easily find the input x. +
    • +
    • + Collision attacks. If you know a hash value h(x), + you should not be able to easily find a different input + y + with the same hash value h(x) = h(y). +
    • +
    + +

    + As an example, both MD5 and SHA-1 are known to be vulnerable to collision attacks. +

    + +

    + Since it's OK to use a weak cryptographic hash function in a non-security + context, this query only alerts when these are used to hash sensitive + data (such as passwords, certificates, usernames). +

    + +
    + + +

    + Ensure that you use a strong, modern cryptographic hash function, such as: +

    + +
      +
    • + Argon2, scrypt, bcrypt, or PBKDF2 for passwords and other data with limited input space where + a dictionary-like attack is feasible. +
    • +
    • + SHA-2, or SHA-3 in other cases. +
    • +
    + +
    + + +

    + The following examples show a function for checking whether the hash + of a certificate matches a known value -- to prevent tampering. + + In the first case the MD5 hashing algorithm is used that is known to be vulnerable to collision attacks. +

    + +

    + + Here is the same function using SHA-512, which is a strong cryptographic hashing function. +

    + + +
    + +
  • + OWASP: + Password Storage + Cheat Sheet + + and + + Transport Layer Protection Cheat Sheet + +
  • +
    + +
    diff --git a/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.ql b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.ql new file mode 100755 index 00000000000..b697a9a01dd --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashing.ql @@ -0,0 +1,63 @@ +/** + * @name Use of a broken or weak cryptographic hashing algorithm on sensitive data + * @description Using broken or weak cryptographic hashing algorithms can compromise security. + * @kind path-problem + * @problem.severity warning + * @security-severity 7.5 + * @precision high + * @id swift/weak-sensitive-data-hashing + * @tags security + * external/cwe/cwe-327 + * external/cwe/cwe-328 + */ + +import swift +import codeql.swift.security.SensitiveExprs +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import DataFlow::PathGraph + +class WeakHashingConfig extends TaintTracking::Configuration { + WeakHashingConfig() { this = "WeakHashingConfig" } + + override predicate isSource(DataFlow::Node node) { node instanceof WeakHashingConfig::Source } + + override predicate isSink(DataFlow::Node node) { node instanceof WeakHashingConfig::Sink } +} + +module WeakHashingConfig { + class Source extends DataFlow::Node { + Source() { this.asExpr() instanceof SensitiveExpr } + } + + abstract class Sink extends DataFlow::Node { + abstract string getAlgorithm(); + } + + class CryptoHash extends Sink { + string algorithm; + + CryptoHash() { + exists(ApplyExpr call, FuncDecl func | + call.getAnArgument().getExpr() = this.asExpr() and + call.getStaticTarget() = func and + func.getName().matches(["hash(%", "update(%"]) and + algorithm = func.getEnclosingDecl().(StructDecl).getName() and + algorithm = ["MD5", "SHA1"] + ) + } + + override string getAlgorithm() { result = algorithm } + } +} + +from + WeakHashingConfig config, DataFlow::PathNode source, DataFlow::PathNode sink, string algorithm, + SensitiveExpr expr +where + config.hasFlowPath(source, sink) and + algorithm = sink.getNode().(WeakHashingConfig::Sink).getAlgorithm() and + expr = source.getNode().asExpr() +select sink.getNode(), source, sink, + "Insecure hashing algorithm (" + algorithm + ") depends on $@.", source.getNode(), + "sensitive data (" + expr.getSensitiveType() + " " + expr.getLabel() + ")" diff --git a/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingBad.swift b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingBad.swift new file mode 100755 index 00000000000..a39dd47edce --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingBad.swift @@ -0,0 +1,5 @@ +typealias Hasher = Crypto.Insecure.MD5 + +func checkCertificate(cert: Array[UInt8], hash: Array[UInt8]) -> Bool + return Hasher.hash(data: cert) == hash // BAD +} diff --git a/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingGood.swift b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingGood.swift new file mode 100755 index 00000000000..7345b2ea49c --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-328/WeakSensitiveDataHashingGood.swift @@ -0,0 +1,4 @@ +typealias Hasher = Crypto.SHA512 + +func checkCertificate(cert: Array[UInt8], hash: Array[UInt8]) -> Bool + return Hasher.hash(data: cert) == hash // GOOD diff --git a/swift/ql/src/queries/Security/CWE-757/InsecureTLS.qhelp b/swift/ql/src/queries/Security/CWE-757/InsecureTLS.qhelp new file mode 100644 index 00000000000..aa403b0153b --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-757/InsecureTLS.qhelp @@ -0,0 +1,28 @@ + + + +

    TLS v1.0 and v1.1 versions are known to be vulnerable.

    +
    + + +

    Use tls_protocol_version_t.TLSv12 or tls_protocol_version_t.TLSv13 when configuring URLSession.

    + +
    + + +

    Specify a newer tls_protocol_version_t explicitly, or omit it completely as the OS will use secure defaults.

    + + + +
    + + +
  • + Apple Platform Security - TLS security + Preventing Insecure Network Connections +
  • + +
    +
    diff --git a/swift/ql/src/queries/Security/CWE-757/InsecureTLS.ql b/swift/ql/src/queries/Security/CWE-757/InsecureTLS.ql new file mode 100644 index 00000000000..375da521dc0 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-757/InsecureTLS.ql @@ -0,0 +1,55 @@ +/** + * @name Insecure TLS configuration + * @description TLS v1.0 and v1.1 versions are known to be vulnerable. TLS v1.2 or v1.3 should be used instead. + * @kind path-problem + * @problem.severity error + * @security-severity 7.5 + * @precision high + * @id swift/insecure-tls + * @tags security + * external/cwe/cwe-757 + */ + +import swift +import codeql.swift.dataflow.DataFlow +import codeql.swift.dataflow.TaintTracking +import codeql.swift.dataflow.FlowSources +import DataFlow::PathGraph + +/** + * A taint config to detect insecure configuration of `NSURLSessionConfiguration` + */ +class InsecureTlsConfig extends TaintTracking::Configuration { + InsecureTlsConfig() { this = "InsecureTLSConfig" } + + /** + * Holds for enum values that represent an insecure version of TLS + */ + override predicate isSource(DataFlow::Node node) { + exists(MethodRefExpr expr, EnumElementDecl enum, string enumName | + node.asExpr() = expr and + expr.getMember() = enum and + enumName = enum.getName() and + enumName in ["TLSv10", "TLSv11", "tlsProtocol10", "tlsProtocol11"] + ) + } + + /** + * Holds for assignment of TLS-related properties of `NSURLSessionConfiguration` + */ + override predicate isSink(DataFlow::Node node) { + exists(AssignExpr assign, MemberRefExpr member, string memberName | + assign.getSource() = node.asExpr() and + assign.getDest() = member and + memberName = member.getMember().(ConcreteVarDecl).getName() and + memberName in [ + "tlsMinimumSupportedProtocolVersion", "tlsMinimumSupportedProtocol", + "tlsMaximumSupportedProtocolVersion", "tlsMaximumSupportedProtocol" + ] + ) + } +} + +from InsecureTlsConfig config, DataFlow::PathNode sourceNode, DataFlow::PathNode sinkNode +where config.hasFlowPath(sourceNode, sinkNode) +select sinkNode.getNode(), sourceNode, sinkNode, "This TLS configuration is insecure." diff --git a/swift/ql/src/queries/Security/CWE-757/SecureTLS.swift b/swift/ql/src/queries/Security/CWE-757/SecureTLS.swift new file mode 100644 index 00000000000..0254638e824 --- /dev/null +++ b/swift/ql/src/queries/Security/CWE-757/SecureTLS.swift @@ -0,0 +1,12 @@ +// Set TLS version explicitly +func createURLSession() -> URLSession { + let config = URLSessionConfiguration.default + config.tlsMinimumSupportedProtocolVersion = tls_protocol_version_t.TLSv13 + return URLSession(configuration: config) +} + +// Use the secure OS defaults +func createURLSession() -> URLSession { + let config = URLSessionConfiguration.default + return URLSession(configuration: config) +} diff --git a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected index 2b5c779957c..35dd1683be3 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected +++ b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected @@ -1,13 +1,8 @@ | testCoreData.swift:48:15:48:15 | password | label:password, type:credential | -| testCoreData.swift:49:15:49:15 | password_hash | isProbablySafe, label:password_hash, type:credential | | testCoreData.swift:51:24:51:24 | password | label:password, type:credential | -| testCoreData.swift:52:24:52:24 | password_hash | isProbablySafe, label:password_hash, type:credential | | testCoreData.swift:58:15:58:15 | password | label:password, type:credential | -| testCoreData.swift:59:15:59:15 | password_file | isProbablySafe, label:password_file, type:credential | | testCoreData.swift:61:25:61:25 | password | label:password, type:credential | -| testCoreData.swift:62:25:62:25 | password_file | isProbablySafe, label:password_file, type:credential | | testCoreData.swift:64:16:64:16 | password | label:password, type:credential | -| testCoreData.swift:65:16:65:16 | password_file | isProbablySafe, label:password_file, type:credential | | testCoreData.swift:77:2:77:25 | call to doSomething(password:) | label:doSomething(password:), type:credential | | testCoreData.swift:77:24:77:24 | x | label:password, type:credential | | testCoreData.swift:80:10:80:22 | call to getPassword() | label:getPassword(), type:credential | @@ -16,15 +11,10 @@ | testCoreData.swift:92:10:92:10 | passwd | label:passwd, type:credential | | testCoreData.swift:93:10:93:10 | passwd | label:passwd, type:credential | | testRealm.swift:34:11:34:11 | myPassword | label:myPassword, type:credential | -| testRealm.swift:38:11:38:11 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential | | testRealm.swift:42:11:42:11 | myPassword | label:myPassword, type:credential | -| testRealm.swift:46:11:46:11 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential | | testRealm.swift:52:12:52:12 | myPassword | label:myPassword, type:credential | -| testRealm.swift:55:12:55:12 | myHashedPassword | isProbablySafe, label:myHashedPassword, type:credential | | testSend.swift:29:19:29:19 | passwordPlain | label:passwordPlain, type:credential | -| testSend.swift:30:19:30:19 | passwordHash | isProbablySafe, label:passwordHash, type:credential | | testSend.swift:33:19:33:19 | passwordPlain | label:passwordPlain, type:credential | -| testSend.swift:34:19:34:19 | passwordHash | isProbablySafe, label:passwordHash, type:credential | | testSend.swift:45:13:45:13 | password | label:password, type:credential | | testSend.swift:46:13:46:13 | password | label:password, type:credential | | testSend.swift:47:17:47:17 | password | label:password, type:credential | @@ -32,6 +22,5 @@ | testSend.swift:49:27:49:27 | password | label:password, type:credential | | testSend.swift:50:27:50:27 | password | label:password, type:credential | | testURL.swift:13:54:13:54 | passwd | label:passwd, type:credential | -| testURL.swift:14:54:14:54 | encrypted_passwd | isProbablySafe, label:encrypted_passwd, type:credential | | testURL.swift:16:55:16:55 | credit_card_no | label:credit_card_no, type:private information | | testURL.swift:20:22:20:22 | passwd | label:passwd, type:credential | diff --git a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.ql b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.ql index 5caa85c948f..3694468b130 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.ql +++ b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.ql @@ -5,8 +5,6 @@ string describe(SensitiveExpr e) { result = "label:" + e.getLabel() or result = "type:" + e.getSensitiveType().toString() - or - e.isProbablySafe() and result = "isProbablySafe" } from SensitiveExpr e diff --git a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift index c2881b613b1..2fe008f0b6f 100644 --- a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift +++ b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift @@ -18,5 +18,5 @@ func test1(passwd : String, encrypted_passwd : String, account_no : String, cred let base = URL(string: "http://example.com/"); // GOOD (not sensitive) let e = URL(string: "abc", relativeTo: base); // GOOD (not sensitive) let f = URL(string: passwd, relativeTo: base); // BAD - let g = URL(string: "abc", relativeTo: f); // BAD [NOT DETECTED] + let g = URL(string: "abc", relativeTo: f); // BAD (reported on line above) } diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected new file mode 100644 index 00000000000..06ee4968d01 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected @@ -0,0 +1,42 @@ +edges +| testCrypto.swift:56:47:56:47 | passwd : | testCrypto.swift:63:44:63:44 | passwd | +| testCrypto.swift:60:43:60:43 | credit_card_no : | testCrypto.swift:61:43:61:43 | credit_card_no | +| testCrypto.swift:60:43:60:43 | credit_card_no : | testCrypto.swift:61:43:61:43 | credit_card_no : | +| testCrypto.swift:60:43:60:43 | credit_card_no : | testCrypto.swift:67:44:67:44 | credit_card_no | +| testCrypto.swift:61:43:61:43 | credit_card_no : | testCrypto.swift:67:44:67:44 | credit_card_no | +nodes +| testCrypto.swift:56:47:56:47 | passwd | semmle.label | passwd | +| testCrypto.swift:56:47:56:47 | passwd : | semmle.label | passwd : | +| testCrypto.swift:60:43:60:43 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:60:43:60:43 | credit_card_no : | semmle.label | credit_card_no : | +| testCrypto.swift:61:43:61:43 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:61:43:61:43 | credit_card_no : | semmle.label | credit_card_no : | +| testCrypto.swift:63:44:63:44 | passwd | semmle.label | passwd | +| testCrypto.swift:67:44:67:44 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:90:23:90:23 | passwd | semmle.label | passwd | +| testCrypto.swift:94:23:94:23 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:99:23:99:23 | passwd | semmle.label | passwd | +| testCrypto.swift:103:23:103:23 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:132:32:132:32 | passwd | semmle.label | passwd | +| testCrypto.swift:136:32:136:32 | credit_card_no | semmle.label | credit_card_no | +| testCrypto.swift:141:32:141:32 | passwd | semmle.label | passwd | +| testCrypto.swift:145:32:145:32 | credit_card_no | semmle.label | credit_card_no | +subpaths +#select +| testCrypto.swift:56:47:56:47 | passwd | testCrypto.swift:56:47:56:47 | passwd | testCrypto.swift:56:47:56:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:56:47:56:47 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:60:43:60:43 | credit_card_no | testCrypto.swift:60:43:60:43 | credit_card_no | testCrypto.swift:60:43:60:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:60:43:60:43 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:61:43:61:43 | credit_card_no | testCrypto.swift:60:43:60:43 | credit_card_no : | testCrypto.swift:61:43:61:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:60:43:60:43 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:61:43:61:43 | credit_card_no | testCrypto.swift:61:43:61:43 | credit_card_no | testCrypto.swift:61:43:61:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:61:43:61:43 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:63:44:63:44 | passwd | testCrypto.swift:56:47:56:47 | passwd : | testCrypto.swift:63:44:63:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:56:47:56:47 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:63:44:63:44 | passwd | testCrypto.swift:63:44:63:44 | passwd | testCrypto.swift:63:44:63:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:63:44:63:44 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:67:44:67:44 | credit_card_no | testCrypto.swift:60:43:60:43 | credit_card_no : | testCrypto.swift:67:44:67:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:60:43:60:43 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:67:44:67:44 | credit_card_no | testCrypto.swift:61:43:61:43 | credit_card_no : | testCrypto.swift:67:44:67:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:61:43:61:43 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:67:44:67:44 | credit_card_no | testCrypto.swift:67:44:67:44 | credit_card_no | testCrypto.swift:67:44:67:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:67:44:67:44 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:90:23:90:23 | passwd | testCrypto.swift:90:23:90:23 | passwd | testCrypto.swift:90:23:90:23 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:90:23:90:23 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:94:23:94:23 | credit_card_no | testCrypto.swift:94:23:94:23 | credit_card_no | testCrypto.swift:94:23:94:23 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:94:23:94:23 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:99:23:99:23 | passwd | testCrypto.swift:99:23:99:23 | passwd | testCrypto.swift:99:23:99:23 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:99:23:99:23 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:103:23:103:23 | credit_card_no | testCrypto.swift:103:23:103:23 | credit_card_no | testCrypto.swift:103:23:103:23 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:103:23:103:23 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:132:32:132:32 | passwd | testCrypto.swift:132:32:132:32 | passwd | testCrypto.swift:132:32:132:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:132:32:132:32 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:136:32:136:32 | credit_card_no | testCrypto.swift:136:32:136:32 | credit_card_no | testCrypto.swift:136:32:136:32 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCrypto.swift:136:32:136:32 | credit_card_no | sensitive data (private information credit_card_no) | +| testCrypto.swift:141:32:141:32 | passwd | testCrypto.swift:141:32:141:32 | passwd | testCrypto.swift:141:32:141:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:141:32:141:32 | passwd | sensitive data (credential passwd) | +| testCrypto.swift:145:32:145:32 | credit_card_no | testCrypto.swift:145:32:145:32 | credit_card_no | testCrypto.swift:145:32:145:32 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCrypto.swift:145:32:145:32 | credit_card_no | sensitive data (private information credit_card_no) | diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref new file mode 100644 index 00000000000..85270fde299 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.qlref @@ -0,0 +1 @@ +queries/Security/CWE-328/WeakSensitiveDataHashing.ql diff --git a/swift/ql/test/query-tests/Security/CWE-328/testCrypto.swift b/swift/ql/test/query-tests/Security/CWE-328/testCrypto.swift new file mode 100644 index 00000000000..a97eab7501d --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-328/testCrypto.swift @@ -0,0 +1,170 @@ +//codeql-extractor-options: -module-name Crypto + +struct SHA256 { + static func hash(data: D) -> [UInt8] { + return [] + } + + func update(data: D) {} + func update(bufferPointer: UnsafeRawBufferPointer) {} + func finalize() -> [UInt8] { return [] } +} + +struct SHA384 { + static func hash(data: D) -> [UInt8] { + return [] + } + + func update(data: D) {} + func update(bufferPointer: UnsafeRawBufferPointer) {} + func finalize() -> [UInt8] { return [] } +} + +struct SHA512 { + static func hash(data: D) -> [UInt8] { + return [] + } + + func update(data: D) {} + func update(bufferPointer: UnsafeRawBufferPointer) {} + func finalize() -> [UInt8] { return [] } +} + + +enum Insecure { + struct MD5 { + static func hash(data: D) -> [UInt8] { + return [] + } + + func update(data: D) {} + func update(bufferPointer: UnsafeRawBufferPointer) {} + func finalize() -> [UInt8] { return [] } + } + struct SHA1 { + static func hash(data: D) -> [UInt8] { + return [] + } + + func update(data: D) {} + func update(bufferPointer: UnsafeRawBufferPointer) {} + func finalize() -> [UInt8] { return [] } + } +} + +func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD + hash = Crypto.Insecure.MD5.hash(data: cert) // BAD [NOT DETECTED] + hash = Crypto.Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive) + hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD [NOT DETECTED] + hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD + hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD + + hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD + hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD [NOT DETECTED] + hash = Crypto.Insecure.SHA1.hash(data: encrypted_passwd) // GOOD (not sensitive) + hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD [NOT DETECTED] + hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD + + hash = Crypto.SHA256.hash(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash = Crypto.SHA256.hash(data: cert) // GOOD, computationally expensive hash not required + hash = Crypto.SHA256.hash(data: account_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA256.hash(data: credit_card_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA256.hash(data: credit_card_no) // GOOD, computationally expensive hash not required + + hash = Crypto.SHA384.hash(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash = Crypto.SHA384.hash(data: cert) // GOOD, computationally expensive hash not required + hash = Crypto.SHA384.hash(data: account_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA384.hash(data: credit_card_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA384.hash(data: credit_card_no) // GOOD, computationally expensive hash not required + + hash = Crypto.SHA512.hash(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash = Crypto.SHA512.hash(data: cert) // GOOD, computationally expensive hash not required + hash = Crypto.SHA512.hash(data: account_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA512.hash(data: credit_card_no) // GOOD, computationally expensive hash not required + hash = Crypto.SHA512.hash(data: credit_card_no) // GOOD, computationally expensive hash not required +} + +func testMD5UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.Insecure.MD5() + hash.update(data: passwd) // BAD + hash.update(data: cert) // BAD [NOT DETECTED] + hash.update(data: encrypted_passwd) // GOOD (not sensitive) + hash.update(data: account_no) // BAD [NOT DETECTED] + hash.update(data: credit_card_no) // BAD +} + +func testSHA1UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.Insecure.SHA1() + hash.update(data: passwd) // BAD + hash.update(data: cert) // BAD [NOT DETECTED] + hash.update(data: encrypted_passwd) // GOOD (not sensitive) + hash.update(data: account_no) // BAD [NOT DETECTED] + hash.update(data: credit_card_no) // BAD +} + +func testSHA256UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.SHA256() + hash.update(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(data: cert) // GOOD + hash.update(data: account_no) // GOOD + hash.update(data: credit_card_no) // GOOD +} + +func testSHA384UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.SHA384() + hash.update(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(data: cert) // GOOD + hash.update(data: account_no) // GOOD + hash.update(data: credit_card_no) // GOOD +} + +func testSHA512UpdateWithData(passwd : String, cert: String, encrypted_passwd : String, account_no : String, credit_card_no : String) { + var hash = Crypto.SHA512() + hash.update(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(data: cert) // GOOD + hash.update(data: account_no) // GOOD + hash.update(data: credit_card_no) // GOOD +} + +func testMD5UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { + var hash = Crypto.Insecure.MD5() + hash.update(bufferPointer: passwd) // BAD + hash.update(bufferPointer: cert) // BAD [NOT DETECTED] + hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) + hash.update(bufferPointer: account_no) // BAD [NOT DETECTED] + hash.update(bufferPointer: credit_card_no) // BAD +} + +func testSHA1UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { + var hash = Crypto.Insecure.SHA1() + hash.update(bufferPointer: passwd) // BAD + hash.update(bufferPointer: cert) // BAD [NOT DETECTED] + hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive) + hash.update(bufferPointer: account_no) // BAD [NOT DETECTED] + hash.update(bufferPointer: credit_card_no) // BAD +} + +func testSHA256UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { + var hash = Crypto.SHA256() + hash.update(bufferPointer: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(bufferPointer: cert) // GOOD + hash.update(bufferPointer: account_no) // GOOD + hash.update(bufferPointer: credit_card_no) // GOOD +} + +func testSHA384UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { + var hash = Crypto.SHA384() + hash.update(bufferPointer: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(bufferPointer: cert) // GOOD + hash.update(bufferPointer: account_no) // GOOD + hash.update(bufferPointer: credit_card_no) // GOOD +} + +func testSHA512UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, cert: UnsafeRawBufferPointer, encrypted_passwd : UnsafeRawBufferPointer, account_no : UnsafeRawBufferPointer, credit_card_no : UnsafeRawBufferPointer) { + var hash = Crypto.SHA512() + hash.update(bufferPointer: passwd) // BAD [NOT DETECTED] not a computationally expensive hash + hash.update(bufferPointer: cert) // GOOD + hash.update(bufferPointer: account_no) // GOOD + hash.update(bufferPointer: credit_card_no) // GOOD +} diff --git a/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.expected b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.expected new file mode 100644 index 00000000000..c6d2599be5e --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.expected @@ -0,0 +1,86 @@ +edges +| InsecureTLS.swift:19:7:19:7 | value : | file://:0:0:0:0 | value | +| InsecureTLS.swift:20:7:20:7 | value : | file://:0:0:0:0 | value | +| InsecureTLS.swift:22:7:22:7 | value : | file://:0:0:0:0 | value | +| InsecureTLS.swift:23:7:23:7 | value : | file://:0:0:0:0 | value | +| InsecureTLS.swift:40:47:40:70 | .TLSv10 : | InsecureTLS.swift:19:7:19:7 | value : | +| InsecureTLS.swift:45:47:45:70 | .TLSv11 : | InsecureTLS.swift:19:7:19:7 | value : | +| InsecureTLS.swift:57:47:57:70 | .TLSv10 : | InsecureTLS.swift:20:7:20:7 | value : | +| InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 : | InsecureTLS.swift:22:7:22:7 | value : | +| InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 : | InsecureTLS.swift:23:7:23:7 | value : | +| InsecureTLS.swift:102:10:102:33 | .TLSv10 : | InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() | +| InsecureTLS.swift:102:10:102:33 | .TLSv10 : | InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() : | +| InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() : | InsecureTLS.swift:19:7:19:7 | value : | +| InsecureTLS.swift:121:55:121:66 | version : | InsecureTLS.swift:122:47:122:47 | version | +| InsecureTLS.swift:121:55:121:66 | version : | InsecureTLS.swift:122:47:122:47 | version : | +| InsecureTLS.swift:122:47:122:47 | version : | InsecureTLS.swift:19:7:19:7 | value : | +| InsecureTLS.swift:127:25:127:48 | .TLSv11 : | InsecureTLS.swift:121:55:121:66 | version : | +| InsecureTLS.swift:158:7:158:7 | self [TLSVersion] : | file://:0:0:0:0 | self [TLSVersion] : | +| InsecureTLS.swift:158:7:158:7 | value : | file://:0:0:0:0 | value : | +| InsecureTLS.swift:163:3:163:3 | [post] def [TLSVersion] : | InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | +| InsecureTLS.swift:163:20:163:43 | .TLSv10 : | InsecureTLS.swift:158:7:158:7 | value : | +| InsecureTLS.swift:163:20:163:43 | .TLSv10 : | InsecureTLS.swift:163:3:163:3 | [post] def [TLSVersion] : | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | InsecureTLS.swift:158:7:158:7 | self [TLSVersion] : | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | InsecureTLS.swift:165:47:165:51 | .TLSVersion | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | InsecureTLS.swift:165:47:165:51 | .TLSVersion : | +| InsecureTLS.swift:165:47:165:51 | .TLSVersion : | InsecureTLS.swift:19:7:19:7 | value : | +| file://:0:0:0:0 | self [TLSVersion] : | file://:0:0:0:0 | .TLSVersion : | +| file://:0:0:0:0 | value : | file://:0:0:0:0 | [post] self [TLSVersion] : | +nodes +| InsecureTLS.swift:19:7:19:7 | value : | semmle.label | value : | +| InsecureTLS.swift:20:7:20:7 | value : | semmle.label | value : | +| InsecureTLS.swift:22:7:22:7 | value : | semmle.label | value : | +| InsecureTLS.swift:23:7:23:7 | value : | semmle.label | value : | +| InsecureTLS.swift:40:47:40:70 | .TLSv10 | semmle.label | .TLSv10 | +| InsecureTLS.swift:40:47:40:70 | .TLSv10 : | semmle.label | .TLSv10 : | +| InsecureTLS.swift:45:47:45:70 | .TLSv11 | semmle.label | .TLSv11 | +| InsecureTLS.swift:45:47:45:70 | .TLSv11 : | semmle.label | .TLSv11 : | +| InsecureTLS.swift:57:47:57:70 | .TLSv10 | semmle.label | .TLSv10 | +| InsecureTLS.swift:57:47:57:70 | .TLSv10 : | semmle.label | .TLSv10 : | +| InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 | semmle.label | .tlsProtocol10 | +| InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 : | semmle.label | .tlsProtocol10 : | +| InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 | semmle.label | .tlsProtocol10 | +| InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 : | semmle.label | .tlsProtocol10 : | +| InsecureTLS.swift:102:10:102:33 | .TLSv10 : | semmle.label | .TLSv10 : | +| InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() | semmle.label | call to getBadTLSVersion() | +| InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() : | semmle.label | call to getBadTLSVersion() : | +| InsecureTLS.swift:121:55:121:66 | version : | semmle.label | version : | +| InsecureTLS.swift:122:47:122:47 | version | semmle.label | version | +| InsecureTLS.swift:122:47:122:47 | version : | semmle.label | version : | +| InsecureTLS.swift:127:25:127:48 | .TLSv11 : | semmle.label | .TLSv11 : | +| InsecureTLS.swift:158:7:158:7 | self [TLSVersion] : | semmle.label | self [TLSVersion] : | +| InsecureTLS.swift:158:7:158:7 | value : | semmle.label | value : | +| InsecureTLS.swift:163:3:163:3 | [post] def [TLSVersion] : | semmle.label | [post] def [TLSVersion] : | +| InsecureTLS.swift:163:20:163:43 | .TLSv10 : | semmle.label | .TLSv10 : | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | semmle.label | def [TLSVersion] : | +| InsecureTLS.swift:165:47:165:51 | .TLSVersion | semmle.label | .TLSVersion | +| InsecureTLS.swift:165:47:165:51 | .TLSVersion : | semmle.label | .TLSVersion : | +| file://:0:0:0:0 | .TLSVersion : | semmle.label | .TLSVersion : | +| file://:0:0:0:0 | [post] self [TLSVersion] : | semmle.label | [post] self [TLSVersion] : | +| file://:0:0:0:0 | self [TLSVersion] : | semmle.label | self [TLSVersion] : | +| file://:0:0:0:0 | value | semmle.label | value | +| file://:0:0:0:0 | value | semmle.label | value | +| file://:0:0:0:0 | value | semmle.label | value | +| file://:0:0:0:0 | value | semmle.label | value | +| file://:0:0:0:0 | value : | semmle.label | value : | +subpaths +| InsecureTLS.swift:163:20:163:43 | .TLSv10 : | InsecureTLS.swift:158:7:158:7 | value : | file://:0:0:0:0 | [post] self [TLSVersion] : | InsecureTLS.swift:163:3:163:3 | [post] def [TLSVersion] : | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | InsecureTLS.swift:158:7:158:7 | self [TLSVersion] : | file://:0:0:0:0 | .TLSVersion : | InsecureTLS.swift:165:47:165:51 | .TLSVersion | +| InsecureTLS.swift:165:47:165:47 | def [TLSVersion] : | InsecureTLS.swift:158:7:158:7 | self [TLSVersion] : | file://:0:0:0:0 | .TLSVersion : | InsecureTLS.swift:165:47:165:51 | .TLSVersion : | +#select +| InsecureTLS.swift:40:47:40:70 | .TLSv10 | InsecureTLS.swift:40:47:40:70 | .TLSv10 | InsecureTLS.swift:40:47:40:70 | .TLSv10 | This TLS configuration is insecure. | +| InsecureTLS.swift:45:47:45:70 | .TLSv11 | InsecureTLS.swift:45:47:45:70 | .TLSv11 | InsecureTLS.swift:45:47:45:70 | .TLSv11 | This TLS configuration is insecure. | +| InsecureTLS.swift:57:47:57:70 | .TLSv10 | InsecureTLS.swift:57:47:57:70 | .TLSv10 | InsecureTLS.swift:57:47:57:70 | .TLSv10 | This TLS configuration is insecure. | +| InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 | InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 | InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 | This TLS configuration is insecure. | +| InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 | InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 | InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 | This TLS configuration is insecure. | +| InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() | InsecureTLS.swift:102:10:102:33 | .TLSv10 : | InsecureTLS.swift:111:47:111:64 | call to getBadTLSVersion() | This TLS configuration is insecure. | +| InsecureTLS.swift:122:47:122:47 | version | InsecureTLS.swift:127:25:127:48 | .TLSv11 : | InsecureTLS.swift:122:47:122:47 | version | This TLS configuration is insecure. | +| InsecureTLS.swift:165:47:165:51 | .TLSVersion | InsecureTLS.swift:163:20:163:43 | .TLSv10 : | InsecureTLS.swift:165:47:165:51 | .TLSVersion | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:40:47:40:70 | .TLSv10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:45:47:45:70 | .TLSv11 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:57:47:57:70 | .TLSv10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:64:40:64:52 | .tlsProtocol10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:76:40:76:52 | .tlsProtocol10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:102:10:102:33 | .TLSv10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:127:25:127:48 | .TLSv11 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | +| file://:0:0:0:0 | value | InsecureTLS.swift:163:20:163:43 | .TLSv10 : | file://:0:0:0:0 | value | This TLS configuration is insecure. | diff --git a/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.qlref b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.qlref new file mode 100644 index 00000000000..af89770b496 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.qlref @@ -0,0 +1 @@ +queries/Security/CWE-757/InsecureTLS.ql diff --git a/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.swift b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.swift new file mode 100644 index 00000000000..d06c9614ac3 --- /dev/null +++ b/swift/ql/test/query-tests/Security/CWE-757/InsecureTLS.swift @@ -0,0 +1,173 @@ +// Stubs + +enum tls_protocol_version_t : UInt16 { + case TLSv10 + case TLSv11 + case TLSv12 + case TLSv13 +} + +enum SSLProtocol { + case tlsProtocol10 + case tlsProtocol11 + case tlsProtocol12 + case tlsProtocol13 +} + +class URLSessionConfiguration { + init() {} + var tlsMinimumSupportedProtocolVersion: tls_protocol_version_t = tls_protocol_version_t.TLSv12 + var tlsMaximumSupportedProtocolVersion: tls_protocol_version_t = tls_protocol_version_t.TLSv13 + + var tlsMinimumSupportedProtocol: SSLProtocol = SSLProtocol.tlsProtocol12 + var tlsMaximumSupportedProtocol: SSLProtocol = SSLProtocol.tlsProtocol13 +} + +/// tlsMinimumSupportedProtocolVersion + +func case_0() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = tls_protocol_version_t.TLSv12 // GOOD +} + +func case_1() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = tls_protocol_version_t.TLSv13 // GOOD +} + +func case_2() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = tls_protocol_version_t.TLSv10 // BAD +} + +func case_3() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = tls_protocol_version_t.TLSv11 // BAD +} + +/// tlsMaximumSupportedProtocolVersion + +func case_4() { + let config = URLSessionConfiguration() + config.tlsMaximumSupportedProtocolVersion = tls_protocol_version_t.TLSv12 // GOOD +} + +func case_5() { + let config = URLSessionConfiguration() + config.tlsMaximumSupportedProtocolVersion = tls_protocol_version_t.TLSv10 // BAD +} + +/// tlsMinimumSupportedProtocol + +func case_6() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocol = SSLProtocol.tlsProtocol10 // BAD +} + +func case_7() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocol = SSLProtocol.tlsProtocol12 // GOOD +} + +/// tlsMaximumSupportedProtocol + +func case_8() { + let config = URLSessionConfiguration() + config.tlsMaximumSupportedProtocol = SSLProtocol.tlsProtocol10 // BAD +} + +func case_9() { + let config = URLSessionConfiguration() + config.tlsMaximumSupportedProtocol = SSLProtocol.tlsProtocol12 // GOOD +} + +/// Indirect assignment (global vars) + +let badGlobalVersion = tls_protocol_version_t.TLSv10 +let goodGlobalVersion = tls_protocol_version_t.TLSv12 + +func case_10() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = badGlobalVersion // BAD [not detected] +} + +func case_11() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = goodGlobalVersion // GOOD +} + +/// Indirect assignment (function calls) + +func getBadTLSVersion() -> tls_protocol_version_t { + return tls_protocol_version_t.TLSv10 +} + +func getGoodTLSVersion() -> tls_protocol_version_t { + return tls_protocol_version_t.TLSv13 +} + +func case_12() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = getBadTLSVersion() // BAD +} + +func case_13() { + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = getGoodTLSVersion() // GOOD +} + +/// Indirect assignment (via call arguments) + +func setTLSVersion(_ config: URLSessionConfiguration, _ version: tls_protocol_version_t) { + config.tlsMinimumSupportedProtocolVersion = version +} + +func case_14() { + let config = URLSessionConfiguration() + setTLSVersion(config, tls_protocol_version_t.TLSv11) // BAD +} + +func case_15() { + let config = URLSessionConfiguration() + setTLSVersion(config, tls_protocol_version_t.TLSv13) // GOOD +} + +/// Indirect assignment (via external entity) + +struct BadDefault { + let TLSVersion: tls_protocol_version_t = tls_protocol_version_t.TLSv11 +} + +func case_16() { + let def = BadDefault() + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = def.TLSVersion // BAD [not detected] +} + +struct GoodDefault { + let TLSVersion: tls_protocol_version_t = tls_protocol_version_t.TLSv12 +} + +func case_17() { + let def = GoodDefault() + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = def.TLSVersion // GOOD +} + +struct VarDefault { + var TLSVersion: tls_protocol_version_t = tls_protocol_version_t.TLSv12 +} + +func case_18() { + var def = VarDefault() + def.TLSVersion = tls_protocol_version_t.TLSv10 + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = def.TLSVersion // BAD +} + +func case_19() { + var def = VarDefault() + def.TLSVersion = tls_protocol_version_t.TLSv13 + let config = URLSessionConfiguration() + config.tlsMinimumSupportedProtocolVersion = def.TLSVersion // GOOD +} diff --git a/swift/tools/tracing-config.lua b/swift/tools/tracing-config.lua index d9343285099..4644457515e 100644 --- a/swift/tools/tracing-config.lua +++ b/swift/tools/tracing-config.lua @@ -29,6 +29,8 @@ function RegisterExtractorPack(id) strip_unsupported_arg(args, '-emit-localized-strings', 0) strip_unsupported_arg(args, '-emit-localized-strings-path', 1) strip_unsupported_arg(args, '-stack-check', 0) + strip_unsupported_arg(args, '-disable-clang-spi', 0) + strip_unsupported_arg(args, '-empty-abi-descriptor', 0) end -- xcodebuild does not always specify the -resource-dir in which case the compiler falls back