diff --git a/javascript/ql/lib/change-notes/2026-10-29-shared-ssa.md b/javascript/ql/lib/change-notes/2026-10-29-shared-ssa.md new file mode 100644 index 000000000000..fbb9181d3fd3 --- /dev/null +++ b/javascript/ql/lib/change-notes/2026-10-29-shared-ssa.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* JavaScript and TypeScript data flow now use the shared SSA implementation, including refinements and conservative captured-variable definitions. The existing public SSA APIs remain available as shared-backed adapters; the separate JavaScript SSA computation has been removed. This improves barrier-guard handling at control-flow joins, reducing false positives when guarded and overwritten values merge, and improves the ordering of post-update flow around calls and assignments. diff --git a/javascript/ql/lib/semmle/javascript/SSA.qll b/javascript/ql/lib/semmle/javascript/SSA.qll index 04d08c2343f6..06c79deedaa7 100644 --- a/javascript/ql/lib/semmle/javascript/SSA.qll +++ b/javascript/ql/lib/semmle/javascript/SSA.qll @@ -1,410 +1,42 @@ /** - * Provides classes for working with static single assignment form (SSA). + * Provides the JavaScript-facing static single assignment (SSA) API. * - * We compute SSA form based on the intra-procedural CFG, without - * any call graph information. This means that we have to make worst-case - * assumptions about the possible effects of function calls and `yield`: + * Definitions, liveness, phi insertion, and reaching definitions are computed + * by the shared SSA library. These classes retain the JavaScript API and + * presentation of explicit writes, implicit initializations, captures, and + * value-preserving refinements. * - * - For a variable `x` declared in a function `f`, if `x` has assignments - * in a function other than `f`, then any function call and `yield` - * expression is assumed to write `x`. - * - If `x` is not written outside `f`, then function calls can never - * affect `x`, while `yield` expressions in functions other than `f` - * still may affect it. - * - * This is modeled as follows. - * - * Within each function `g` that accesses a variable `x` declared in an - * enclosing function `f`, we introduce a pseudo-assignment to `x` called - * a _capture_ of `x` at the beginning of `g` that (conceptually) captures - * the current value of `x`. - * - * Additionally, we introduce _re-captures_ for `x` in the following - * places: - * - * - At any function call and `yield`, if `x` is assigned outside `f`. - * - At any `yield` outside `f`, if `x` is not assigned outside `f`. - * - * Re-captures are introduced only where needed, that is, where there - * is a live use of `x` after the re-capture. - * - * To see why re-captures need to be placed at `yield` expressions, - * consider the following function: - * - * ``` - * function k() { - * var x = 0; - * - * function* iter() { - * console.log(x); - * yield; - * console.log(x); - * } - * - * var gen = iter(); - * gen.next(); - * ++x; - * gen.next(); - * } - * ``` - * - * Here, `iter` has a capture for `x` at its beginning, and a re-capture - * at the `yield` to reflect the fact that `x` is incremented between the - * two `console.log` calls. - * - * In the above example, `x` is only assigned inside its declaring function - * `k`, so function calls and `yield` expressions inside `k` cannot affect it. - * - * Consider another example: - * - * ``` - * function* k() { - * var x = 0; - * console.log(x); - * yield () => ++x; - * console.log(x); - * } - * var gen = k(); - * gen.next().value(); - * gen.next(); - * ``` - * - * Here, `x` is assigned outside its declaring function `k`, so the `yield` - * expression in `k` induces a re-capture of `x` to reflect the fact that `x` - * is incremented between the two `console.log` calls. + * Captured variables are conservatively recaptured at entry to a capturing + * function, at yields, and at calls that may observe non-local assignments. + * The global data-flow library separately uses shared variable-capture flow + * where more precise call-graph information is available. */ overlay[local?] module; import javascript private import semmle.javascript.dataflow.Refinements -private import semmle.javascript.internal.CachedStages +private import semmle.javascript.dataflow.internal.sharedlib.Ssa as Shared -/** - * A variable that can be SSA converted, that is, a local variable. - */ +/** A variable that can be SSA converted, that is, a local variable. */ class SsaSourceVariable extends LocalVariable { } -cached -private module Internal { - /** - * A data type representing SSA definitions. - * - * We distinguish five kinds of SSA definitions: - * - * 1. Explicit definitions wrapping a `VarDef` node in the CFG. - * 2. Implicit initializations of locals (including `arguments`) at - * the start of a function, which do not correspond directly to - * CFG nodes. - * 3. Pseudo-definitions for captured variables at the beginning of - * the capturing function as well as after `yield` expressions - * and calls. - * 4. Phi nodes. - * 5. Refinement nodes at points in the CFG where additional information - * about a variable becomes available, which may constrain the set of - * its potential values. - * - * SSA definitions are only introduced where necessary. In particular, - * unreachable code has no SSA definitions associated with it, and neither - * have dead assignments (that is, assignments whose value is never read). - */ - cached - newtype TSsaDefinition = - TExplicitDef(ReachableBasicBlock bb, int i, VarDef d, SsaSourceVariable v, VarRef lhs) { - bb.defAt(i, v, d, lhs) and - ( - liveAfterDef(bb, i, v) or - v.isCaptured() - ) - } or - TImplicitInit(EntryBasicBlock bb, SsaSourceVariable v) { - bb.getContainer() = v.getDeclaringContainer().getFunctionBoundary() and - (liveAtEntry(bb, v) or v.isCaptured()) - } or - TCapture(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - mayCapture(bb, i, v) and liveAfterDef(bb, i, v) - } or - TPhi(ReachableJoinBlock bb, SsaSourceVariable v) { - liveAtEntry(bb, v) and - inDefDominanceFrontier(bb, v) - } or - TRefinement(ReachableBasicBlock bb, int i, GuardControlFlowNode guard, SsaSourceVariable v) { - bb.getNode(i) = guard and - guard.getTest().(Refinement).getRefinedVar() = v and - liveAtEntry(bb, v) - } - - /** - * Holds if `bb` is in the dominance frontier of a block containing a definition of `v`. - */ - pragma[noinline] - private predicate inDefDominanceFrontier(ReachableJoinBlock bb, SsaSourceVariable v) { - exists(ReachableBasicBlock defbb, SsaDefinition def | - def.definesAt(defbb, _, v) and - bb.inDominanceFrontierOf(defbb) - ) - } - - /** - * Holds if `v` is a captured variable which is declared in `declContainer` and read in - * `useContainer`. - */ - private predicate readsCapturedVar( - StmtContainer useContainer, SsaSourceVariable v, StmtContainer declContainer - ) { - declContainer = v.getDeclaringContainer() and - useContainer = any(VarUse u | u.getVariable() = v).getContainer() and - v.isCaptured() - } - - /** - * Holds if the `i`th node of `bb` in container `sc` is entry node `nd`. - */ - private predicate entryNode( - StmtContainer sc, ReachableBasicBlock bb, int i, ControlFlowEntryNode nd - ) { - sc = bb.getContainer() and bb.getNode(i) = nd - } - - /** - * Holds if the `i`th node of `bb` in container `sc` is yield expression `nd`. - */ - private predicate yieldNode(StmtContainer sc, ReachableBasicBlock bb, int i, YieldExpr nd) { - sc = bb.getContainer() and bb.getNode(i) = nd - } - - /** - * Holds if the `i`th node of `bb` in container `sc` is invocation expression `nd`. - */ - private predicate invokeNode(StmtContainer sc, ReachableBasicBlock bb, int i, InvokeExpr nd) { - sc = bb.getContainer() and bb.getNode(i) = nd - } - - /** - * Holds if the `i`th node of basic block `bb` may induce a pseudo-definition for - * modeling updates to captured variable `v`. Whether the definition is actually - * introduced depends on whether `v` is live at this point in the program. - */ - private predicate mayCapture(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(ControlFlowNode nd, StmtContainer capturingContainer, StmtContainer declContainer | - // capture initial value of variable declared in enclosing scope - readsCapturedVar(capturingContainer, v, declContainer) and - capturingContainer != declContainer and - entryNode(capturingContainer, bb, i, nd) - or - // re-capture value of variable after `yield` if it is declared in enclosing scope - // or assigned non-locally - readsCapturedVar(capturingContainer, v, declContainer) and - (capturingContainer != declContainer or assignedThroughClosure(v)) and - yieldNode(capturingContainer, bb, i, nd) - or - // re-capture value of variable after a call if it is assigned non-locally - readsCapturedVar(capturingContainer, v, declContainer) and - assignedThroughClosure(v) and - invokeNode(capturingContainer, bb, i, nd) - ) - } - - /** - * A classification of variable references into reads and writes. - */ - cached - newtype RefKind = - Read() or - Write() - - /** - * Holds if the `i`th node of basic block `bb` is a reference to `v`, either a read - * (when `tp` is `Read()`) or a direct or indirect write (when `tp` is `Write()`). - */ - private predicate ref(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind tp) { - bb.useAt(i, v, _) and tp = Read() - or - (mayCapture(bb, i, v) or bb.defAt(i, v, _)) and - tp = Write() - } - - /** - * Gets the (1-based) rank of the reference to `v` at the `i`th node of basic block `bb`, - * which has the given reference kind `tp`. - */ - private int refRank(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind tp) { - i = rank[result](int j | ref(bb, j, v, _)) and - ref(bb, i, v, tp) - } - - /** - * Gets the maximum rank among all references to `v` in basic block `bb`. - */ - private int maxRefRank(ReachableBasicBlock bb, SsaSourceVariable v) { - result = max(refRank(bb, _, v, _)) - } +final private class NativeDefinition = Shared::Definition; - /** - * Holds if variable `v` is live after the `i`th node of basic block `bb`, where - * `i` is the index of a node that may assign or capture `v`. - * - * For the purposes of this predicate, `yield` expressions and function invocations - * are considered as writes of captured variables. - */ - private predicate liveAfterDef(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = refRank(bb, i, v, Write()) | - // the next reference to `v` inside `bb` is a read - r + 1 = refRank(bb, _, v, Read()) - or - // this is the last reference to `v` inside `bb`, but `v` is live at entry - // to a successor basic block of `bb` - r = maxRefRank(bb, v) and - liveAtSuccEntry(bb, v) - ) - } - - /** - * Holds if variable `v` is live at the beginning of basic block `bb`. - * - * For the purposes of this predicate, `yield` expressions and function invocations - * are considered as writes of captured variables. - */ - private predicate liveAtEntry(ReachableBasicBlock bb, SsaSourceVariable v) { - // the first reference to `v` inside `bb` is a read - refRank(bb, _, v, Read()) = 1 - or - // there is no reference to `v` inside `bb`, but `v` is live at entry - // to a successor basic block of `bb` - not exists(refRank(bb, _, v, _)) and - liveAtSuccEntry(bb, v) - } - - /** - * Holds if `v` is live at the beginning of any successor of basic block `bb`. - */ - private predicate liveAtSuccEntry(ReachableBasicBlock bb, SsaSourceVariable v) { - liveAtEntry(bb.getASuccessor(), v) - } - - /** - * Holds if `v` is assigned outside its declaring function. - */ - private predicate assignedThroughClosure(SsaSourceVariable v) { - v.getAnAccess().(LValue).getContainer() != v.getDeclaringContainer() - } - - /** - * Holds if the `i`th node of `bb` is a use or an SSA definition of variable `v`, with - * `k` indicating whether it is the former or the latter. - */ - private predicate ssaRef(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind k) { - bb.useAt(i, v, _) and k = Read() - or - any(SsaDefinition def).definesAt(bb, i, v) and k = Write() - } - - /** - * Gets the (1-based) rank of the `i`th node of `bb` among all SSA definitions - * and uses of `v` in `bb`, with `k` indicating whether it is a definition or a use. - * - * For example, if `bb` is a basic block with a phi node for `v` (considered - * to be at index -1), uses `v` at node 2 and defines it at node 5, we have: - * - * ``` - * ssaRefRank(bb, -1, v, Write()) = 1 // phi node - * ssaRefRank(bb, 2, v, Read()) = 2 // use at node 2 - * ssaRefRank(bb, 5, v, Write()) = 3 // definition at node 5 - * ``` - */ - private int ssaRefRank(ReachableBasicBlock bb, int i, SsaSourceVariable v, RefKind k) { - i = rank[result](int j | ssaRef(bb, j, v, _)) and - ssaRef(bb, i, v, k) - } - - /** - * Gets the minimum rank of a read in `bb` such that all references to `v` between that - * read and the read at index `i` are reads (and not writes). - */ - private int rewindReads(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = ssaRefRank(bb, i, v, Read()) | - exists(int j, RefKind k | r - 1 = ssaRefRank(bb, j, v, k) | - k = Read() and result = rewindReads(bb, j, v) - or - k = Write() and result = r - ) - or - r = 1 and result = r - ) - } - - /** - * Gets the SSA definition of `v` in `bb` that reaches the read of `v` at node `i`, if any. - */ - private SsaDefinition getLocalDefinition(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - exists(int r | r = rewindReads(bb, i, v) | - exists(int j | result.definesAt(bb, j, v) and ssaRefRank(bb, j, v, _) = r - 1) - ) - } - - /** - * Gets an SSA definition of `v` that reaches the end of the immediate dominator of `bb`. - */ - pragma[noinline] - private SsaDefinition getDefReachingEndOfImmediateDominator( - ReachableBasicBlock bb, SsaSourceVariable v - ) { - result = getDefReachingEndOf(bb.getImmediateDominator(), v) - } - - /** - * Gets an SSA definition of `v` that reaches the end of basic block `bb`. - */ - cached - SsaDefinition getDefReachingEndOf(ReachableBasicBlock bb, SsaSourceVariable v) { - Stages::DataFlowStage::ref() and - exists(int lastRef | lastRef = max(int i | ssaRef(bb, i, v, _)) | - result = getLocalDefinition(bb, lastRef, v) - or - result.definesAt(bb, lastRef, v) and - liveAtSuccEntry(bb, v) - ) - or - /* - * In SSA form, the (unique) reaching definition of a use is the closest - * definition that dominates the use. If two definitions dominate a node - * then one must dominate the other, so we can find the reaching definition - * by following the idominance relation backwards. - */ - - result = getDefReachingEndOfImmediateDominator(bb, v) and - not exists(SsaDefinition ssa | ssa.definesAt(bb, _, v)) and - liveAtSuccEntry(bb, v) - } - - /** - * Gets the unique SSA definition of `v` whose value reaches the `i`th node of `bb`, - * which is a use of `v`. - */ - cached - SsaDefinition getDefinition(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - result = getLocalDefinition(bb, i, v) - or - rewindReads(bb, i, v) = 1 and result = getDefReachingEndOf(bb.getImmediateDominator(), v) - } -} - -private import Internal - -/** - * An SSA variable. - */ -class SsaVariable extends TSsaDefinition { +/** An SSA variable, represented by its shared SSA definition. */ +class SsaVariable instanceof SsaDefinition { /** Gets the source variable corresponding to this SSA variable. */ - SsaSourceVariable getSourceVariable() { result = this.(SsaDefinition).getSourceVariable() } + SsaSourceVariable getSourceVariable() { result = this.getDefinition().getSourceVariable() } - /** Gets the (unique) definition of this SSA variable. */ + /** Gets the unique definition of this SSA variable. */ SsaDefinition getDefinition() { result = this } /** Gets a use in basic block `bb` that refers to this SSA variable. */ VarUse getAUseIn(ReachableBasicBlock bb) { - exists(int i, SsaSourceVariable v | v = this.getSourceVariable() | - bb.useAt(i, v, result) and this = getDefinition(bb, i, v) + exists(int i, Shared::SsaConfig::SourceVariable variable | + variable.asLocalVariable() = this.getSourceVariable() and + bb.useAt(i, this.getSourceVariable(), result) and + Shared::ssaDefReachesRead(variable, this, bb, i) ) } @@ -417,15 +49,7 @@ class SsaVariable extends TSsaDefinition { /** Gets the location of this SSA variable. */ Location getLocation() { result = this.getDefinition().getLocation() } - /** - * DEPRECATED. Use `getLocation().hasLocationInfo()` instead. - * - * 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/). - */ + /** DEPRECATED. Use `getLocation().hasLocationInfo()` instead. */ deprecated predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { @@ -433,66 +57,48 @@ class SsaVariable extends TSsaDefinition { } } -/** - * An SSA definition. - */ -class SsaDefinition extends TSsaDefinition { +/** An SSA definition backed by the shared SSA implementation. */ +class SsaDefinition instanceof NativeDefinition { + SsaDefinition() { exists(this.(NativeDefinition).getSourceVariable().asLocalVariable()) } + /** Gets the SSA variable defined by this definition. */ SsaVariable getVariable() { result = this } /** Gets the source variable defined by this definition. */ - abstract SsaSourceVariable getSourceVariable(); + SsaSourceVariable getSourceVariable() { + result = this.(NativeDefinition).getSourceVariable().asLocalVariable() + } - /** - * Gets the basic block to which this definition belongs. - * - * Currently, all SSA definitions belong to a basic block, but the representation of - * implicit definitions might change in the future, making this no longer true. - */ - abstract ReachableBasicBlock getBasicBlock(); + /** Gets the basic block to which this definition belongs. */ + ReachableBasicBlock getBasicBlock() { result = this.(NativeDefinition).getBasicBlock() } /** * INTERNAL: Use `getBasicBlock()` and `getSourceVariable()` instead. * - * Holds if this is a definition of source variable `v` at index `idx` in basic block `bb`. - * - * Phi nodes are considered to be at index `-1`, all other definitions at the index of - * the control flow node they correspond to. - */ - abstract predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v); + * Holds if this defines `v` at index `idx` in `bb`. Implicit initializations + * retain their JavaScript API index of zero; shared SSA stores them at -1. + */ + predicate definesAt(ReachableBasicBlock bb, int idx, SsaSourceVariable v) { + exists(Shared::SsaConfig::SourceVariable variable, int nativeIndex | + this.(NativeDefinition).definesAt(variable, bb, nativeIndex) and + variable.asLocalVariable() = v and + if this instanceof SsaImplicitInit then idx = 0 else idx = nativeIndex + ) + } - /** - * Gets a variable definition node whose value may end up contributing - * to the SSA variable defined by this definition. - */ + /** Gets a variable definition contributing to this SSA definition. */ abstract VarDef getAContributingVarDef(); - /** - * INTERNAL: Use `toString()` instead. - * - * Gets a pretty-printed representation of this SSA definition. - */ + /** INTERNAL: Use `toString()` instead. */ abstract string prettyPrintDef(); - /** - * INTERNAL: Do not use. - * - * Gets a pretty-printed representation of a reference to this SSA definition. - */ + /** INTERNAL: Do not use. */ abstract string prettyPrintRef(); /** Gets a textual representation of this element. */ string toString() { result = this.prettyPrintDef() } - /** - * DEPRECATED. Use `getLocation().hasLocationInfo()` instead. - * - * 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/). - */ + /** DEPRECATED. Use `getLocation().hasLocationInfo()` instead. */ deprecated predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn ) { @@ -506,33 +112,42 @@ class SsaDefinition extends TSsaDefinition { StmtContainer getContainer() { result = this.getBasicBlock().getContainer() } } -/** - * An SSA definition that corresponds to an explicit assignment or other variable definition. - */ -class SsaExplicitDefinition extends SsaDefinition, TExplicitDef { - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - this = TExplicitDef(bb, i, _, v, _) +/** An SSA definition corresponding to an explicit variable definition. */ +class SsaExplicitDefinition extends SsaDefinition { + SsaExplicitDefinition() { + exists(Shared::SsaConfig::SourceVariable variable, BasicBlock bb, int i | + this.(Shared::WriteDefinition).definesAt(variable, bb, i) and + bb.defAt(i, variable.asLocalVariable(), _) + ) } - /** This SSA definition corresponds to the definition of `v` at `def`. */ - predicate defines(VarDef def, SsaSourceVariable v) { this = TExplicitDef(_, _, def, v, _) } + /** Holds if this corresponds to the definition of `v` at `def`. */ + predicate defines(VarDef def, SsaSourceVariable v) { + def = this.getDef() and v = this.getSourceVariable() + } /** Gets the variable definition wrapped by this SSA definition. */ - VarDef getDef() { this = TExplicitDef(_, _, result, _, _) } - - /** Gets the variable reference appearing on the left-hand side of this assignment. */ - VarRef getLhs() { this = TExplicitDef(_, _, _, _, result) } - - /** Gets the basic block to which this definition belongs. */ - override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) } + VarDef getDef() { + exists(Shared::SsaConfig::SourceVariable variable, BasicBlock bb, int i | + this.(Shared::WriteDefinition).definesAt(variable, bb, i) and + bb.defAt(i, variable.asLocalVariable(), result) + ) + } - override SsaSourceVariable getSourceVariable() { this = TExplicitDef(_, _, _, result, _) } + /** Gets the variable reference on the left-hand side of this assignment. */ + VarRef getLhs() { + exists(Shared::SsaConfig::SourceVariable variable, BasicBlock bb, int i | + this.(Shared::WriteDefinition).definesAt(variable, bb, i) and + bb.defAt(i, variable.asLocalVariable(), this.getDef(), result) + ) + } override VarDef getAContributingVarDef() { result = this.getDef() } override string prettyPrintRef() { - exists(int l, int c | this.getLocation().hasLocationInfo(_, l, c, _, _) | - result = "def@" + l + ":" + c + exists(int line, int column | + this.getLocation().hasLocationInfo(_, line, column, _, _) and + result = "def@" + line + ":" + column ) } @@ -540,45 +155,28 @@ class SsaExplicitDefinition extends SsaDefinition, TExplicitDef { override Location getLocation() { result = this.getLhs().getLocation() } - /** - * Gets the data flow node representing the incoming value assigned at this definition, - * if any. - */ + /** Gets the incoming value assigned at this definition, if any. */ DataFlow::Node getRhsNode() { result = DataFlow::ssaDefinitionNode(this).getImmediatePredecessor() } } -/** - * An SSA definition that does not correspond to an explicit variable definition. - */ +/** An SSA definition that does not correspond to an explicit variable definition. */ abstract class SsaImplicitDefinition extends SsaDefinition { - /** - * INTERNAL: Do not use. - * - * Gets the definition kind to include in `prettyPrintRef`. - */ + /** INTERNAL: Gets the definition kind used in `prettyPrintRef`. */ abstract string getKind(); override string prettyPrintRef() { - exists(int l, int c | this.getLocation().hasLocationInfo(_, l, c, _, _) | - result = this.getKind() + "@" + l + ":" + c + exists(int line, int column | + this.getLocation().hasLocationInfo(_, line, column, _, _) and + result = this.getKind() + "@" + line + ":" + column ) } } -/** - * An SSA definition representing the implicit initialization of a variable - * at the beginning of its scope. - */ -class SsaImplicitInit extends SsaImplicitDefinition, TImplicitInit { - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - bb = this.getBasicBlock() and v = this.getSourceVariable() and i = 0 - } - - override ReachableBasicBlock getBasicBlock() { this = TImplicitInit(result, _) } - - override SsaSourceVariable getSourceVariable() { this = TImplicitInit(_, result) } +/** The implicit initialization of a variable at the beginning of its scope. */ +class SsaImplicitInit extends SsaImplicitDefinition { + SsaImplicitInit() { this.(Shared::WriteDefinition).definesAt(_, any(EntryBasicBlock bb), -1) } override string getKind() { result = "implicitInit" } @@ -589,21 +187,9 @@ class SsaImplicitInit extends SsaImplicitDefinition, TImplicitInit { } } -/** - * An SSA definition representing the capturing of an SSA-convertible variable - * in the closure of a nested function. - * - * Capturing definitions appear at the beginning of such functions, as well as - * at any `yield` expressions or calls that may affect the value of the variable. - */ -class SsaVariableCapture extends SsaImplicitDefinition, TCapture { - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - this = TCapture(bb, i, v) - } - - override ReachableBasicBlock getBasicBlock() { this.definesAt(result, _, _) } - - override SsaSourceVariable getSourceVariable() { this.definesAt(_, _, result) } +/** A conservative capture or recapture of a variable from an enclosing scope. */ +class SsaVariableCapture extends SsaImplicitDefinition { + SsaVariableCapture() { Shared::isCaptureDefinition(this) } override VarDef getAContributingVarDef() { result.getAVariable() = this.getSourceVariable() } @@ -612,16 +198,9 @@ class SsaVariableCapture extends SsaImplicitDefinition, TCapture { override string prettyPrintDef() { result = "capture variable " + this.getSourceVariable() } } -/** - * An SSA definition that has no actual semantics, but simply serves to - * merge or filter data flow. - * - * Phi nodes are the canonical example. - */ +/** A definition that merges or refines incoming SSA values. */ abstract class SsaPseudoDefinition extends SsaImplicitDefinition { - /** - * Gets an input of this pseudo-definition. - */ + /** Gets an input of this pseudo-definition. */ cached abstract SsaVariable getAnInput(); @@ -629,99 +208,55 @@ abstract class SsaPseudoDefinition extends SsaImplicitDefinition { result = this.getAnInput().getDefinition().getAContributingVarDef() } - /** - * Gets a textual representation of the inputs of this pseudo-definition - * in lexicographical order. - */ + /** Gets a textual representation of the inputs in lexicographical order. */ string ppInputs() { result = concat(this.getAnInput().getDefinition().prettyPrintRef(), ", ") } } -/** - * An SSA phi node, that is, a pseudo-definition for a variable at a point - * in the flow graph where otherwise two or more definitions for the variable - * would be visible. - */ -class SsaPhiNode extends SsaPseudoDefinition, TPhi { - /** - * Gets the input to this phi node coming from the given predecessor block. - */ +/** A phi definition from the shared SSA implementation. */ +class SsaPhiNode extends SsaPseudoDefinition instanceof Shared::PhiNode { + /** Gets the input arriving from predecessor block `bb`. */ cached - SsaVariable getInputFromBlock(BasicBlock bb) { - bb = this.getBasicBlock().getAPredecessor() and - result = getDefReachingEndOf(bb, this.getSourceVariable()) - } + SsaVariable getInputFromBlock(BasicBlock bb) { Shared::phiHasInputFromBlock(this, result, bb) } override SsaVariable getAnInput() { result = this.getInputFromBlock(_) } - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - bb = this.getBasicBlock() and v = this.getSourceVariable() and i = -1 - } - - override ReachableBasicBlock getBasicBlock() { this = TPhi(result, _) } - - override SsaSourceVariable getSourceVariable() { this = TPhi(_, result) } - override string getKind() { result = "phi" } override string prettyPrintDef() { result = this.getSourceVariable() + " = phi(" + this.ppInputs() + ")" } - /** - * If all inputs to this phi node are (transitive) refinements of the same variable, - * gets that variable. - */ + /** Gets the variable of which all inputs are transitive refinements, if any. */ SsaVariable getRephinedVariable() { forex(SsaVariable input | input = this.getAnInput() | result = getRefinedVariable(input)) } } -/** - * Gets the input to the given refinement node or rephinement node. - */ -private SsaVariable getRefinedVariable(SsaVariable v) { - result = getRefinedVariable(v.(SsaRefinementNode).getAnInput()) +private SsaVariable getRefinedVariable(SsaVariable variable) { + result = getRefinedVariable(variable.(SsaRefinementNode).getAnInput()) or - result = getRefinedVariable(v.(SsaPhiNode).getRephinedVariable()) + result = getRefinedVariable(variable.(SsaPhiNode).getRephinedVariable()) or - not v instanceof SsaRefinementNode and - not v instanceof SsaPhiNode and - result = v + not variable instanceof SsaRefinementNode and + not variable instanceof SsaPhiNode and + result = variable } -/** - * A refinement node, that is, a pseudo-definition for a variable at a point - * in the flow graph where additional information about this variable becomes - * available that may restrict its possible set of values. - */ -class SsaRefinementNode extends SsaPseudoDefinition, TRefinement { - /** - * Gets the guard that induces this refinement. - */ - GuardControlFlowNode getGuard() { this = TRefinement(_, _, result, _) } +/** A shared uncertain write preserving the incoming value under a refinement. */ +class SsaRefinementNode extends SsaPseudoDefinition { + SsaRefinementNode() { Shared::isRefinementDefinition(this) } - /** - * Gets the refinement associated with this definition. - */ - Refinement getRefinement() { result = this.getGuard().getTest() } - - override SsaVariable getAnInput() { - exists(SsaSourceVariable v, BasicBlock bb | - v = this.getSourceVariable() and bb = this.getBasicBlock() - | - if exists(SsaPhiNode phi | phi.definesAt(bb, _, v)) - then result.(SsaPhiNode).definesAt(bb, _, v) - else result = getDefReachingEndOf(bb.getAPredecessor(), v) + /** Gets the guard inducing this refinement. */ + GuardControlFlowNode getGuard() { + exists(BasicBlock bb, int i | + this.(NativeDefinition).definesAt(_, bb, i) and result = bb.getNode(i) ) } - override predicate definesAt(ReachableBasicBlock bb, int i, SsaSourceVariable v) { - this = TRefinement(bb, i, _, v) - } - - override ReachableBasicBlock getBasicBlock() { this = TRefinement(result, _, _, _) } + /** Gets the refinement associated with this definition. */ + Refinement getRefinement() { result = this.getGuard().getTest() } - override SsaSourceVariable getSourceVariable() { this = TRefinement(_, _, _, result) } + override SsaVariable getAnInput() { Shared::uncertainWriteDefinitionInput(this, result) } override string getKind() { result = "refine[" + this.getGuard() + "]" } @@ -734,7 +269,7 @@ class SsaRefinementNode extends SsaPseudoDefinition, TRefinement { } module Ssa { - /** Gets the SSA definition corresponding to the implicit initialization of `v`. */ + /** Gets the definition of the implicit initialization of `v`. */ SsaImplicitInit implicitInit(SsaSourceVariable v) { result.getSourceVariable() = v } /** Gets the SSA definition corresponding to `d`. */ diff --git a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll index ffbb9e497b04..210e63a3e196 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/Configuration.qll @@ -525,7 +525,12 @@ deprecated private predicate barrierGuardBlocksEdge( SsaVariable input, SsaPhiNode phi, BasicBlock bb, ConditionGuardNode cond, boolean outcome | bb = getADominatedBasicBlock(guard, cond) and - pred = DataFlow::ssaDefinitionNode(input) and + ( + pred = DataFlow::ssaDefinitionNode(input) + or + pred = succ.getAPredecessor() and + pred.getBasicBlock() = bb + ) and succ = DataFlow::ssaDefinitionNode(phi) and input = phi.getInputFromBlock(bb) and outcome = cond.getOutcome() and diff --git a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll index a24d7976b3d6..a692a80e4ad6 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/DataFlow.qll @@ -32,6 +32,7 @@ private import semmle.javascript.dataflow.internal.VariableOrThis private import semmle.javascript.internal.NameResolution private import semmle.javascript.internal.UnderlyingTypes private import semmle.javascript.internal.TypeResolution +private import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2 module DataFlow { /** @@ -279,10 +280,15 @@ module DataFlow { /** * A node in the data flow graph which corresponds to an SSA variable definition. */ - class SsaDefinitionNode extends Node, TSsaDefNode { + class SsaDefinitionNode extends Node { SsaDefinition ssa; - SsaDefinitionNode() { this = TSsaDefNode(ssa) } + SsaDefinitionNode() { + exists(Ssa2::SsaConfig::SourceVariable var, BasicBlock bb, int index | + this = TSharedSsaDefNode(var, bb, index) and + ssa.(Ssa2::Definition).definesAt(var, bb, index) + ) + } /** Gets the SSA variable defined at this data flow node. */ SsaVariable getSsaVariable() { result = ssa.getVariable() } @@ -1441,7 +1447,14 @@ module DataFlow { ExprNode exprNode(Expr e) { result = valueNode(e) } /** Gets the data flow node corresponding to `ssa`. */ - SsaDefinitionNode ssaDefinitionNode(SsaDefinition ssa) { result = TSsaDefNode(ssa) } + SsaDefinitionNode ssaDefinitionNode(SsaDefinition ssa) { result = ssaDefinitionNodeInternal(ssa) } + + private EarlyStageNode ssaDefinitionNodeInternal(SsaDefinition ssa) { + exists(BasicBlock bb, int i, Ssa2::SsaConfig::SourceVariable var | + ssa.(Ssa2::Definition).definesAt(var, bb, i) and + result = TSharedSsaDefNode(var, bb, i) + ) + } /** Gets the node corresponding to the initialization of parameter `p`. */ ParameterNode parameterNode(Parameter p) { result.getParameter() = p } @@ -1525,9 +1538,11 @@ module DataFlow { * before all data flow nodes have been materialised. */ EarlyStageNode lvalueNodeInternal(BindingPattern lvalue) { - exists(SsaExplicitDefinition ssa | - ssa.defines(lvalue.(LValue).getDefNode(), lvalue.(VarRef).getVariable()) and - result = TSsaDefNode(ssa) + exists(Ssa2::Definition ssa, BasicBlock bb, int i, Ssa2::SsaConfig::SourceVariable var | + ssa.definesAt(var, bb, i) and + var.asLocalVariable() = lvalue.(VarRef).getVariable() and + bb.defAt(i, var.asLocalVariable(), lvalue.(LValue).getDefNode(), lvalue) and + result = TSharedSsaDefNode(var, bb, i) ) or result = TValueNode(lvalue.(DestructuringPattern)) @@ -1579,8 +1594,8 @@ module DataFlow { ) or exists(SimpleParameter param | - pred = TValueNode(param) and // The value node represents the incoming argument - succ = lvalueNodeInternal(param) // The SSA node represents the parameters's local variable + pred = TValueNode(param) and + succ = lvalueNodeInternal(param) ) or exists(Expr arg, Parameter param | @@ -1626,18 +1641,69 @@ module DataFlow { ) } - /** - * Flow steps shared between `immediateFlowStep` and `localFlowStep`. - * - * Inlining is forced because the two relations are indexed differently. - */ + private predicate sharedTypeInferenceFlowStep(Ssa2::Node pred, Ssa2::Node succ) { + exists(boolean isUseStep, Ssa2::SsaConfig::SourceVariable variable | + Ssa2::localFlowStep(variable, pred, succ, isUseStep) and + not variable.isCaptured() and + ( + isUseStep = false + or + isUseStep = true and + not sharedReadInsideWith(pred) + ) + ) + or + exists(Ssa2::Node dynamicRead | + sharedReadInsideWith(dynamicRead) and + Ssa2::localFlowStep(_, dynamicRead, succ, true) and + sharedTypeInferenceFlowStep(pred, dynamicRead) + ) + } + + private predicate sharedReadInsideWith(Ssa2::Node node) { + exists(Ssa2::ExprNode read, WithStmt with | + node = read and + not Ssa2::isPostUpdateRead(read) and + read.getExpr().(AstNode).getParent+() = with.getBody() + ) + } + + private predicate sharedImmediateFlowStep(Ssa2::Node pred, Ssa2::Node succ) { + Ssa2::localMustFlowStep(_, pred, succ) + } + + private predicate sharedPhiImmediateFlowStep(EarlyStageNode pred, EarlyStageNode succ) { + exists(SsaPhiNode phi | + not phi.getSourceVariable().isCaptured() and + pred = ssaDefinitionNodeInternal(phi.getRephinedVariable().getDefinition()) and + succ = ssaDefinitionNodeInternal(phi) + ) + } + + private predicate ordinaryFlowStepShared(EarlyStageNode pred, EarlyStageNode succ) { + exists(Ssa2::Node ssaPred, Ssa2::Node ssaSucc, Ssa2::SsaConfig::SourceVariable variable | + Ssa2::localFlowStep(variable, ssaPred, ssaSucc, _) and + not variable.isCaptured() and + pred = getNodeFromSharedSsa(ssaPred) and + succ = getNodeFromSharedSsa(ssaSucc) and + pred != succ + ) + } + pragma[inline] - private predicate immediateFlowStepShared(EarlyStageNode pred, EarlyStageNode succ) { - exists(SsaVariable v | - pred = TSsaDefNode(v.getDefinition()) and - succ = TValueNode(v.getAUse()) + private predicate sharedSsaImmediateFlowStep(EarlyStageNode pred, EarlyStageNode succ) { + exists(Ssa2::Node ssaPred, Ssa2::Node ssaSucc | + sharedImmediateFlowStep(ssaPred, ssaSucc) and + pred = getNodeFromSharedSsa(ssaPred) and + succ = getNodeFromSharedSsa(ssaSucc) and + pred != succ ) or + sharedPhiImmediateFlowStep(pred, succ) + } + + pragma[inline] + private predicate nonSsaImmediateFlowStep(EarlyStageNode pred, EarlyStageNode succ) { exists(Expr predExpr, Expr succExpr | pred = TValueNode(predExpr) and succ = TValueNode(succExpr) | @@ -1666,8 +1732,12 @@ module DataFlow { ) ) or - // flow from 'this' parameter into 'this' expressions + // flow from 'this' parameter into 'this' expressions when not handled by shared SSA exists(ThisExpr thiz | + ( + LocalVariableOrThis::thisInContainer(thiz.getBindingContainer()).isCaptured() or + not thiz.getBasicBlock() instanceof ReachableBasicBlock + ) and pred = TThisNode(thiz.getBindingContainer()) and succ = TValueNode(thiz) ) @@ -1678,11 +1748,28 @@ module DataFlow { succ = TValueNode(call) ) or - // Pass 'this' into implicit uses of 'this' + // Pass 'this' into implicit uses of 'this' when not handled by shared SSA exists(ImplicitThisUse use | + ( + LocalVariableOrThis::thisInContainer(use.getBindingContainer()).isCaptured() or + not use.(ControlFlowNode).getBasicBlock() instanceof ReachableBasicBlock + ) and pred = TThisNode(use.getBindingContainer()) and succ = TImplicitThisUse(use, false) ) + or + exists(SsaVariable captured | + captured.getSourceVariable().isCaptured() and + pred = capturedSsaNode(captured.getDefinition()) and + succ = TValueNode(captured.getAUse()) + ) + } + + pragma[inline] + private predicate immediateFlowStepShared(EarlyStageNode pred, EarlyStageNode succ) { + sharedSsaImmediateFlowStep(pred, succ) + or + nonSsaImmediateFlowStep(pred, succ) } pragma[nomagic] @@ -1692,15 +1779,12 @@ module DataFlow { or immediateFlowStepShared(pred, succ) or - // Refinement of variable -> original definition of variable - exists(SsaRefinementNode refinement | - succ = TSsaDefNode(refinement) and - pred = TSsaDefNode(refinement.getAnInput()) - ) + refinementFlowStep(pred, succ, true) or exists(SsaPhiNode phi | - succ = TSsaDefNode(phi) and - pred = TSsaDefNode(phi.getRephinedVariable()) + phi.getSourceVariable().isCaptured() and + succ = capturedSsaNode(phi) and + pred = capturedSsaNode(phi.getRephinedVariable()) ) or // IIFE call -> return value of IIFE @@ -1712,38 +1796,36 @@ module DataFlow { ) } - /** - * Holds if data can flow from `pred` to `succ` in one local step. - */ cached - predicate localFlowStep(EarlyStageNode pred, EarlyStageNode succ) { + private predicate localFlowStepWithoutSharedOrdinary(EarlyStageNode pred, EarlyStageNode succ) { Stages::EarlyDataFlowStage::ref() and // flow from RHS into LHS lvalueFlowStep(pred, succ) or lvalueDefaultFlowStep(pred, succ) or - immediateFlowStepShared(pred, succ) + nonSsaImmediateFlowStep(pred, succ) + or + refinementFlowStep(pred, succ, false) or - // From an assignment or implicit initialization of a captured variable to its flow-insensitive node. exists(SsaDefinition predDef | - pred = TSsaDefNode(predDef) and + predDef.getSourceVariable().isCaptured() and + pred = capturedSsaNode(predDef) and succ = TCapturedVariableNode(predDef.getSourceVariable()) | predDef instanceof SsaExplicitDefinition or predDef instanceof SsaImplicitInit ) or - // From a captured variable node to its flow-sensitive capture nodes - exists(SsaVariableCapture ssaCapture | - pred = TCapturedVariableNode(ssaCapture.getSourceVariable()) and - succ = TSsaDefNode(ssaCapture) + exists(SsaVariableCapture capture | + pred = TCapturedVariableNode(capture.getSourceVariable()) and + succ = capturedSsaNode(capture) ) or - // Flow through implicit SSA nodes - exists(SsaImplicitDefinition ssa | succ = TSsaDefNode(ssa) | - // from the inputs of phi and pi nodes into the node itself - pred = TSsaDefNode(ssa.(SsaPseudoDefinition).getAnInput().getDefinition()) + exists(SsaImplicitDefinition ssa | + ssa.getSourceVariable().isCaptured() and + succ = capturedSsaNode(ssa) and + pred = capturedSsaNode(ssa.(SsaPseudoDefinition).getAnInput().getDefinition()) ) or exists(Expr predExpr, Expr succExpr | @@ -1775,6 +1857,39 @@ module DataFlow { ) } + /** + * Holds if data can flow from `pred` to `succ` in one local step. + */ + cached + predicate localFlowStep(EarlyStageNode pred, EarlyStageNode succ) { + localFlowStepWithoutSharedOrdinary(pred, succ) + or + ordinaryFlowStepShared(pred, succ) + } + + /** + * INTERNAL: Local flow steps imported by global flow. Shared SSA flow is imported separately. + */ + predicate localFlowStepForGlobal(EarlyStageNode pred, EarlyStageNode succ) { + localFlowStepWithoutSharedOrdinary(pred, succ) + } + + /** + * INTERNAL: Local steps for value inference. Skip intermediate reads in `with` + * statements so their reflective uncertainty does not propagate to later reads. + * Security flow must instead visit those reads so their barriers remain effective. + */ + predicate localFlowStepForTypeInference(EarlyStageNode pred, EarlyStageNode succ) { + localFlowStepWithoutSharedOrdinary(pred, succ) + or + exists(Ssa2::Node ssaPred, Ssa2::Node ssaSucc | + sharedTypeInferenceFlowStep(ssaPred, ssaSucc) and + pred = getNodeFromSharedSsa(ssaPred) and + succ = getNodeFromSharedSsa(ssaSucc) and + pred != succ + ) + } + overlay[global] private class ReflectiveParamsStep extends LegacyPreCallGraphStep { override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) { @@ -1823,6 +1938,83 @@ module DataFlow { result = def.getDestructuringSource() } + private predicate refinementFlowStep(EarlyStageNode pred, EarlyStageNode succ, boolean immediate) { + exists(SsaRefinementNode refinement | + ( + immediate = true + or + immediate = false and refinement.getSourceVariable().isCaptured() + ) and + pred = ssaDefinitionNodeInternal(refinement.getAnInput().getDefinition()) and + succ = ssaDefinitionNodeInternal(refinement) + ) + } + + private EarlyStageNode capturedSsaNode(SsaDefinition ssa) { + ssa.getSourceVariable().isCaptured() and + result = ssaDefinitionNodeInternal(ssa) + } + + /** INTERNAL: Gets the JavaScript data flow node corresponding to a shared SSA node. */ + cached + EarlyStageNode getNodeFromSharedSsa(Ssa2::Node node) { + exists(Ssa2::ExprNode read | + node = read and + not Ssa2::isPostUpdateRead(read) and + not read.getExpr() instanceof ImplicitThisUse and + result = TValueNode(read.getExpr()) + ) + or + exists(Ssa2::ExprNode read, ImplicitThisUse use | + node = read and + read.getExpr() = use and + result = TImplicitThisUse(use, false) + ) + or + exists(Ssa2::ExprPostUpdateNode post | + node = post and + not Ssa2::isPostUpdateRead(post.getPreUpdateNode()) and + not post.getExpr() instanceof ImplicitThisUse and + result = TExprPostUpdateNode(post.getExpr()) + ) + or + exists(ImplicitThisUse use | + node.(Ssa2::ExprPostUpdateNode).getExpr() = use and + result = TImplicitThisUse(use, true) + ) + or + result = TSsaSynthReadNode(node) + or + exists(Ssa2::Definition def, Ssa2::SsaConfig::SourceVariable var, BasicBlock bb, int i | + node.(Ssa2::SsaDefinitionNode).getDefinition() = def and + def.definesAt(var, bb, i) and + result = TSharedSsaDefNode(var, bb, i) + ) + or + exists(Ssa2::Definition def, Ssa2::SsaConfig::SourceVariable var, BasicBlock bb | + node.(Ssa2::SsaDefinitionNode).getDefinition() = def and + def.definesAt(var, bb, -1) and + result = TThisNode(var.asThisContainer()) + ) + or + exists( + Ssa2::WriteDefinition def, Ssa2::SsaConfig::SourceVariable var, BasicBlock bb, int i, + VarDef write + | + node.(Ssa2::WriteDefSourceNode).getDefinition() = def and + def.definesAt(var, bb, i) and + bb.defAt(i, var.asLocalVariable(), write) and + write.getTarget() instanceof VarRef and + result = TValueNode(write.getSource()) + ) + or + exists(Ssa2::WriteDefinition def, Ssa2::SsaConfig::SourceVariable var, EntryBasicBlock bb | + node.(Ssa2::WriteDefSourceNode).getDefinition() = def and + def.definesAt(var, bb, -1) and + result = TThisNode(var.asThisContainer()) + ) + } + /** * Holds if the flow information for the node `nd`. * @@ -1837,7 +2029,7 @@ module DataFlow { */ overlay[global] predicate isIncomplete(Node nd, Incompleteness cause) { - exists(SsaVariable ssa | nd = TSsaDefNode(ssa.getDefinition()) | + exists(SsaVariable ssa | nd = ssaDefinitionNode(ssa.getDefinition()) | defIsIncomplete(ssa.(SsaExplicitDefinition).getDef(), cause) or exists(Variable v | v = ssa.getSourceVariable() | diff --git a/javascript/ql/lib/semmle/javascript/dataflow/TypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/TypeInference.qll index a5e686a90c25..6556331dba93 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/TypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/TypeInference.qll @@ -40,10 +40,10 @@ import internal.VariableTypeInference */ class AnalyzedNode extends DataFlow::Node { /** - * Gets another data flow node whose value flows into this node in one local step - * (that is, not involving global variables). + * Gets a predecessor used for local value inference, without propagating + * reflective uncertainty out of reads in `with` statements. */ - AnalyzedNode localFlowPred() { result = this.getAPredecessor() } + AnalyzedNode localFlowPred() { DataFlow::localFlowStepForTypeInference(result, this) } /** * Gets an abstract value that this node may evaluate to at runtime. diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/BarrierGuards.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/BarrierGuards.qll index 699cfd94fbbc..7a47ac5ca8b9 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/BarrierGuards.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/BarrierGuards.qll @@ -335,8 +335,10 @@ module MakeStateBarrierGuard< Ssa2::SsaDataflowInput::Guard guard, Ssa2::SsaDataflowInput::Expr test, Boolean branch, FlowState state ) { - exists(BarrierGuard g | - g.asExpr() = guard and + exists(BarrierGuard g, ConditionGuardNode cond | + cond.getTest() = guard and + barrierGuardUsedInCondition(g, cond, branch) and + Ssa2::isExplicitRead(test) and g.blocksExpr(branch, test, state) ) } @@ -386,7 +388,15 @@ module MakeStateBarrierGuard< or exists(Expr use | barrierGuardBlocksAccessPathUse(use, state) and - nd = DataFlow::valueNode(use) + ( + nd = DataFlow::valueNode(use) + or + exists(Ssa2::ExprNode read | + read.getExpr() = use and + not Ssa2::isPostUpdateRead(read) and + nd = DataFlowPrivate::getNodeFromSsa2(read) + ) + ) ) or ssa2BlocksNode(nd, state) diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll index 315c8706bc00..6c52a41287c5 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowNode.qll @@ -32,12 +32,21 @@ private module Cached { cached newtype TNode = TValueNode(AST::ValueNode nd) or - /** An SSA node from the legacy SSA library */ - TSsaDefNode(SsaDefinition d) or - /** Use of a variable or 'this', with flow from a post-update node (from an earlier use) */ - TSsaUseNode(ControlFlowNode use) { use = any(Ssa2::SsaConfig::SourceVariable v).getAUse() } or - /** A synthesized variable read (new SSA library). Definitions are represented by TSsaDefNode. */ - TSsaSynthReadNode(Ssa2::SsaSynthReadNode read) or + /** An SSA definition from the shared SSA library */ + TSharedSsaDefNode(Ssa2::SsaConfig::SourceVariable v, BasicBlock bb, int i) { + exists(Ssa2::SsaDefinitionNode node | node.getDefinition().definesAt(v, bb, i)) and + exists(v.asLocalVariable()) + } or + /** A synthesized phi input, phi read, or post-update continuation. */ + TSsaSynthReadNode(Ssa2::Node read) { + not read.(Ssa2::SsaSynthReadNode).getSourceVariable().isCaptured() and + (read instanceof Ssa2::SsaSynthReadNode or Ssa2::isPostUpdateRead(read)) + } or + /** The variable or 'this' value at a read, separate from the expression occurrence. */ + TSharedSsaReadNode(Ssa2::ExprNode read) { + not read.getSourceVariable().isCaptured() and + not Ssa2::isPostUpdateRead(read) + } or TCapturedVariableNode(LocalVariable v) { v.isCaptured() } or TPropNode(@property p) or TRestPatternNode(DestructuringPattern dp, Expr rest) { rest = dp.getRest() } or @@ -130,8 +139,8 @@ private module Cached { import Cached private class TEarlyStageNode = - TValueNode or TSsaDefNode or TCapturedVariableNode or TPropNode or TRestPatternNode or - TElementPatternNode or TElementNode or TReflectiveCallNode or TThisNode or + TValueNode or TSharedSsaDefNode or TSsaSynthReadNode or TCapturedVariableNode or TPropNode or + TRestPatternNode or TElementPatternNode or TElementNode or TReflectiveCallNode or TThisNode or TFunctionSelfReferenceNode or TDestructuredModuleImportNode or THtmlAttributeNode or TFunctionReturnNode or TExceptionalFunctionReturnNode or TExceptionalInvocationReturnNode or TGlobalAccessPathRoot or TTemplatePlaceholderTag or TReflectiveParametersNode or diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll index 1c3eb1dc9e31..ef801fe1495f 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/DataFlowPrivate.qll @@ -23,36 +23,60 @@ private class Node = DataFlow::Node; class PostUpdateNode = DataFlow::PostUpdateNode; -class SsaUseNode extends DataFlow::Node, TSsaUseNode { - private ControlFlowNode expr; +class SsaSynthReadNode extends DataFlow::Node, TSsaSynthReadNode { + private Ssa2::Node read; - SsaUseNode() { this = TSsaUseNode(expr) } + SsaSynthReadNode() { this = TSsaSynthReadNode(read) } cached - override string toString() { result = "[ssa-use] " + expr.toString() } + override string toString() { + exists(string variable | + variable = read.(Ssa2::SsaSynthReadNode).getSourceVariable().getName() + or + variable = read.(Ssa2::ExprNode).getSourceVariable().getName() + | + result = "[ssa-synth-read] " + variable + ) + } cached - override StmtContainer getContainer() { result = expr.getContainer() } + override BasicBlock getBasicBlock() { + result = read.(Ssa2::SsaSynthReadNode).getBasicBlock() + or + result = read.(Ssa2::ExprNode).getExpr().getBasicBlock() + } + + cached + override Location getLocation() { result = read.getLocation() } cached - override Location getLocation() { result = expr.getLocation() } + override File getFile() { result = this.getBasicBlock().getFile() } } -class SsaSynthReadNode extends DataFlow::Node, TSsaSynthReadNode { - private Ssa2::SsaSynthReadNode read; +/** A variable or 'this' value at a read that can flow past a barrier on just one expression occurrence. */ +class SharedSsaReadNode extends DataFlow::Node, TSharedSsaReadNode { + private ControlFlowNode read; - SsaSynthReadNode() { this = TSsaSynthReadNode(read) } + SharedSsaReadNode() { + this = TSharedSsaReadNode(any(Ssa2::ExprNode node | node.getExpr() = read)) + } cached - override string toString() { result = "[ssa-synth-read] " + read.getSourceVariable().getName() } + override string toString() { result = "[ssa-read] " + read.toString() } cached override StmtContainer getContainer() { - result = read.getSourceVariable().getDeclaringContainer() + result = read.(ImplicitThisUse).getUseContainer() + or + not read instanceof ImplicitThisUse and + result = read.getBasicBlock().getContainer() } cached override Location getLocation() { result = read.getLocation() } + + cached + override File getFile() { result = read.getFile() } } class FlowSummaryNode extends DataFlow::Node, TFlowSummaryNode { @@ -680,9 +704,9 @@ predicate nodeIsHidden(Node node) { or node instanceof RestParameterStoreNode or - node instanceof SsaUseNode - or node instanceof SsaSynthReadNode + or + node instanceof TSharedSsaReadNode } predicate neverSkipInPathGraph(Node node) { @@ -1140,9 +1164,6 @@ private predicate legacyBarrier(DataFlow::Node node) { node = MakeBarrierGuard::getABarrierNode() } -/** - * Holds if `node` should be removed from the local data flow graph, for compatibility with legacy code. - */ overlay[global] pragma[nomagic] private predicate isBlockedLegacyNode(Node node) { @@ -1201,7 +1222,10 @@ private predicate imprecisePostUpdateStep(DataFlow::PostUpdateNode postUpdate, D */ overlay[global] private predicate valuePreservingStep(Node node1, Node node2) { - node1.getASuccessor() = node2 and + ( + DataFlow::localFlowStepForGlobal(node1, node2) or + sharedSsaFlow(node1, node2) + ) and not isBlockedLegacyNode(node1) and not isBlockedLegacyNode(node2) and not isThisNodeTrackedByVariableCapture(node1) @@ -1221,50 +1245,76 @@ predicate knownSourceModel(Node sink, string model) { none() } predicate knownSinkModel(Node sink, string model) { none() } -private predicate samePhi(SsaPhiNode legacyPhi, Ssa2::PhiNode newPhi) { - exists(BasicBlock bb, LocalVariableOrThis v | - newPhi.definesAt(v, bb, _) and - legacyPhi.definesAt(bb, _, v.asLocalVariable()) - ) -} - cached Node getNodeFromSsa2(Ssa2::Node node) { - result = TSsaUseNode(node.(Ssa2::ExprNode).getExpr()) + result = DataFlow::getNodeFromSharedSsa(node) or - result = TExprPostUpdateNode(node.(Ssa2::ExprPostUpdateNode).getExpr()) + // Guard barriers validate both an occurrence and the variable's continuing value. + result = TSharedSsaReadNode(node) +} + +private Node getSharedSsaFlowNode(Ssa2::Node node) { + result = TSharedSsaReadNode(node) or - exists(ImplicitThisUse use | - node.(Ssa2::ExprPostUpdateNode).getExpr() = use and - result = TImplicitThisUse(use, true) + (not node instanceof Ssa2::ExprNode or Ssa2::isPostUpdateRead(node)) and + result = DataFlow::getNodeFromSharedSsa(node) +} + +/** + * Gets a source that may carry `origin`'s modeled value through call and heap + * steps. These include copying and cloning models, not just object aliases. + */ +private DataFlow::SourceNode modeledPostUpdateValue( + DataFlow::SourceNode origin, DataFlow::TypeTracker tracker +) { + tracker.start() and + result = origin and + exists(Ssa2::ExprNode read | + Ssa2::additionalPostUpdateContinuation(read, _) and + origin = DataFlow::getNodeFromSharedSsa(read).(Node).getALocalSource() ) or - result = TSsaSynthReadNode(node) - or - exists(SsaPhiNode legacyPhi, Ssa2::PhiNode ssaPhi | - node.(Ssa2::SsaDefinitionNode).getDefinition() = ssaPhi and - samePhi(legacyPhi, ssaPhi) and - result = TSsaDefNode(legacyPhi) + exists(DataFlow::TypeTracker previous | + tracker = previous.step(modeledPostUpdateValue(origin, previous), result) ) } -private predicate useUseFlow(Node node1, Node node2) { - exists(Ssa2::Node ssa1, Ssa2::Node ssa2 | - Ssa2::localFlowStep(_, ssa1, ssa2, _) and - node1 = getNodeFromSsa2(ssa1) and - node2 = getNodeFromSsa2(ssa2) and +private predicate modeledPostUpdateFlow(Node node1, Node node2) { + exists( + Ssa2::ExprNode read, Ssa2::ExprNode continuation, Ssa2::ExprPostUpdateNode post, + Ssa2::Node successor, DataFlow::SourceNode origin + | + Ssa2::additionalPostUpdateContinuation(read, continuation) and + post.getPreUpdateNode() = read and + node1 = DataFlow::getNodeFromSharedSsa(post) and + origin = DataFlow::getNodeFromSharedSsa(read).(Node).getALocalSource() and + modeledPostUpdateValue(origin, DataFlow::TypeTracker::end()) + .flowsTo(DataFlow::getNodeFromSharedSsa(continuation)) and + // Resume after the operation without revisiting its argument or RHS reads. + Ssa2::localFlowStep(continuation.getSourceVariable(), continuation, successor, true) and + node2 = getSharedSsaFlowNode(successor) and not node1.getTopLevel().isExterns() ) - or - exists(Expr use | - node1 = TSsaUseNode(use) and - node2 = TValueNode(use) +} + +private predicate sharedSsaFlow(Node node1, Node node2) { + exists(Ssa2::Node ssaPred, Ssa2::Node ssaSucc, Ssa2::SsaConfig::SourceVariable variable | + Ssa2::localFlowStep(variable, ssaPred, ssaSucc, _) and + not variable.isCaptured() and + node1 = getSharedSsaFlowNode(ssaPred) and + node2 = getSharedSsaFlowNode(ssaSucc) and + node1 != node2 and + not node1.getTopLevel().isExterns() ) or - exists(ImplicitThisUse use | - node1 = TSsaUseNode(use) and - node2 = TImplicitThisUse(use, false) + // A barrier on one use must not sanitize the variable at subsequent uses. + exists(Ssa2::ExprNode read | + node1 = TSharedSsaReadNode(read) and + node2 = DataFlow::getNodeFromSharedSsa(read) and + not node1.getTopLevel().isExterns() ) + or + modeledPostUpdateFlow(node1, node2) } overlay[global] @@ -1278,8 +1328,6 @@ predicate simpleLocalFlowStep(Node node1, Node node2) { nodeGetEnclosingCallable(pragma[only_bind_out](node1)) = nodeGetEnclosingCallable(pragma[only_bind_out](node2)) or - useUseFlow(node1, node2) - or exists(FlowSummaryImpl::Private::SummaryNode input, FlowSummaryImpl::Private::SummaryNode output | FlowSummaryPrivate::Steps::summaryStoreStep(input, MkAwaited(), output) and node1 = TFlowSummaryNode(input) and diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll index 7b479ac475b6..af4540dc1466 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/sharedlib/Ssa.qll @@ -1,7 +1,11 @@ /** - * Instantiates the shared SSA library for JavaScript, but only to establish use-use flow. + * Instantiates the shared SSA library for JavaScript data flow. * - * JavaScript's old SSA library is still responsible for the ordinary SSA flow. + * Refinements are value-preserving uncertain writes, so shared SSA routes flow + * through refinements and their phi inputs. Phi definitions are retained to + * preserve the JavaScript SSA definition-node API. + * Synthetic reads at operation completion carry post-updates past argument + * and right-hand-side evaluation without changing the earlier expression values. */ overlay[local?] module; @@ -10,10 +14,97 @@ private import javascript as js private import codeql.ssa.Ssa private import semmle.javascript.internal.BasicBlockInternal as BasicBlockInternal private import semmle.javascript.dataflow.internal.VariableOrThis +private import semmle.javascript.dataflow.Refinements as Refinements + +private predicate captureAt(js::Cfg::BasicBlock bb, int i, SsaConfig::SourceVariable v) { + exists(js::LocalVariable local, js::StmtContainer container | + local = v.asLocalVariable() and + local.isCaptured() and + container = bb.getContainer() and + any(js::VarUse use | use.getVariable() = local).getContainer() = container + | + bb.getNode(i) instanceof js::ControlFlowEntryNode and + container != local.getDeclaringContainer() + or + bb.getNode(i) instanceof js::YieldExpr and + ( + container != local.getDeclaringContainer() or + local.getAnAccess().(js::LValue).getContainer() != local.getDeclaringContainer() + ) + or + bb.getNode(i) instanceof js::InvokeExpr and + local.getAnAccess().(js::LValue).getContainer() != local.getDeclaringContainer() + ) +} + +private predicate refinementAt(js::Cfg::BasicBlock bb, int i, SsaConfig::SourceVariable v) { + bb.getNode(i).(js::GuardControlFlowNode).getTest().(Refinements::Refinement).getRefinedVar() = + v.asLocalVariable() +} + +private predicate explicitVariableRead(js::ControlFlowNode read, SsaConfig::SourceVariable v) { + read.(js::VarUse).getVariable() = v.asLocalVariable() + or + read.(ThisUse).getBindingContainer() = v.asThisContainer() +} + +/** Gets an operation that may update the object evaluated at `read`. */ +private js::ControlFlowNode postUpdateOperation(js::ControlFlowNode read) { + exists(js::InvokeExpr call | read = call.getAnArgument() or read = call.getCallee() | + result = call + ) + or + exists(js::PropAccess access | read = access.getBase() | + result.(js::VarDef).getTarget() = access + or + not exists(js::VarDef definition | definition.getTarget() = access) and + result = access + or + access = any(js::CompoundAssignExpr assignment).getTarget() and + result = access + or + access = any(js::UpdateExpr update).getOperand().getUnderlyingReference() and + result = access + or + result.(js::MethodCallExpr).getCallee() = access + ) + or + result.(js::AwaitExpr).getOperand() = read + or + exists(js::Assignment assignment | + assignment.getTarget() instanceof js::PropAccess and + assignment.getRhs() = read and + result = assignment + ) + or + read instanceof ImplicitThisUse and + read instanceof js::FieldDefinition and + result = read +} + +// Continuations must not make otherwise dead variable definitions live. +bindingset[bb, i, v] +private predicate liveAfterOperation(js::Cfg::BasicBlock bb, int i, SsaConfig::SourceVariable v) { + exists(v.asThisContainer()) + or + exists(js::LocalVariable local | + local = v.asLocalVariable() and + ( + exists(int j | + j > i and + bb.useAt(j, local, _) and + not exists(int k | i < k and k < j | bb.defAt(k, local, _)) + ) + or + not exists(int k | k > i | bb.defAt(k, local, _)) and + bb.getASuccessor().localIsLiveAtEntry(local) + ) + ) +} module SsaConfig implements InputSig { class SourceVariable extends LocalVariableOrThis { - SourceVariable() { not this.isCaptured() } + SourceVariable() { exists(this.asLocalVariable()) or not this.isCaptured() } } pragma[nomagic] @@ -22,21 +113,50 @@ module SsaConfig implements InputSig { } predicate variableWrite(js::Cfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { - certain = true and + bb instanceof js::ReachableBasicBlock and ( - bb.defAt(i, v.asLocalVariable(), _) + certain = true and + ( + bb.defAt(i, v.asLocalVariable(), _) + or + // Implicit initialization + bb = getEntryBlock(v.getDeclaringContainer().getFunctionBoundary()) and + i = -1 + or + captureAt(bb, i, v) + ) or - // Implicit initialization and function parameters - bb = getEntryBlock(v.getDeclaringContainer()) and - i = -1 + // Refinements preserve the input value but introduce a new SSA definition. + certain = false and + refinementAt(bb, i, v) ) } predicate variableRead(js::Cfg::BasicBlock bb, int i, SourceVariable v, boolean certain) { - bb.useAt(i, v.asLocalVariable(), _) and certain = true - or - certain = true and - bb.getNode(i).(ThisUse).getBindingContainer() = v.asThisContainer() + bb instanceof js::ReachableBasicBlock and + ( + certain = true and + ( + explicitVariableRead(bb.getNode(i), v) + or + not v.isCaptured() and + liveAfterOperation(bb, i, v) and + exists(js::ControlFlowNode read | + explicitVariableRead(read, v) and + postUpdateOperation(read) = bb.getNode(i) + ) + ) + or + // Closures can observe captured writes without a subsequent local read. + certain = false and + v.asLocalVariable().isCaptured() and + ( + bb.defAt(i - 1, v.asLocalVariable(), _) + or + i = 0 and + bb = getEntryBlock(v.getDeclaringContainer().getFunctionBoundary()) + ) + ) } } @@ -46,14 +166,155 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { private import codeql.util.Boolean class Expr extends js::ControlFlowNode { - Expr() { this = any(SsaConfig::SourceVariable v).getAUse() } + Expr() { + this = any(SsaConfig::SourceVariable v).getAUse() + or + exists(js::ControlFlowNode read | + explicitVariableRead(read, _) and + this = postUpdateOperation(read) + ) + } predicate hasCfgNode(js::Cfg::BasicBlock bb, int i) { this = bb.getNode(i) } } predicate ssaDefHasSource(WriteDefinition def) { - // This library only handles use-use flow after a post-update, there are no definitions, only uses. - none() + exists(SsaConfig::SourceVariable v, js::Cfg::BasicBlock bb, int i | + def.definesAt(v, bb, i) and + ( + exists(js::VarDef write | + bb.defAt(i, v.asLocalVariable(), write) and + write.getTarget() instanceof js::VarRef and + exists(write.getSource()) + ) + or + i = -1 and exists(v.asThisContainer()) + ) + ) + } + + predicate allowFlowIntoUncertainDef(UncertainWriteDefinition def) { any() } + + predicate includePhiDefsInFlowStep() { any() } + + // Inferred values depend on this SSA graph, so branch feasibility must remain syntactic. + private predicate createsObject(js::Expr value) { + value.getUnderlyingValue() instanceof js::ObjectExpr or + value.getUnderlyingValue() instanceof js::ArrayExpr or + value.getUnderlyingValue() instanceof js::Function or + value.getUnderlyingValue() instanceof js::ClassDefinition or + value.getUnderlyingValue() instanceof js::RegExpLiteral or + value.getUnderlyingValue() instanceof js::NewExpr + } + + private predicate isKnownTruthy(js::Expr value) { + createsObject(value) + or + value.getUnderlyingValue().(js::BooleanLiteral).getBoolValue() = true + or + value.getUnderlyingValue().(js::NumberLiteral).getValue().toFloat() != 0 + or + value.getUnderlyingValue().(js::StringLiteral).getStringValue() != "" + } + + private predicate isKnownFalsy(js::Expr value) { + value.getUnderlyingValue() instanceof js::NullLiteral or + value.getUnderlyingValue() instanceof js::SyntacticConstants::UndefinedConstant or + value.getUnderlyingValue().(js::BooleanLiteral).getBoolValue() = false or + value.getUnderlyingValue().(js::NumberLiteral).getValue().toFloat() = 0 or + value.getUnderlyingValue().(js::StringLiteral).getStringValue() = "" + } + + /** Gets an expression supplying `value`, excluding tests and discarded operands. */ + private js::Expr getAValueInput(js::Expr value) { + result = value.getUnderlyingValue() + or + exists(js::ConditionalExpr conditional | conditional = value.getUnderlyingValue() | + not isKnownFalsy(conditional.getCondition()) and + result = getAValueInput(conditional.getConsequent()) + or + not isKnownTruthy(conditional.getCondition()) and + result = getAValueInput(conditional.getAlternate()) + ) + or + exists(js::LogicalOrExpr logical | logical = value.getUnderlyingValue() | + result = getAValueInput(logical.getLeftOperand()) + or + not isKnownTruthy(logical.getLeftOperand()) and + result = getAValueInput(logical.getRightOperand()) + ) + or + exists(js::NullishCoalescingExpr coalescing | coalescing = value.getUnderlyingValue() | + result = getAValueInput(coalescing.getLeftOperand()) + or + not createsObject(coalescing.getLeftOperand()) and + not coalescing.getLeftOperand().getUnderlyingValue() instanceof + js::SyntacticConstants::PrimitiveLiteralConstant and + result = getAValueInput(coalescing.getRightOperand()) + ) + or + exists(js::LogicalAndExpr logical | logical = value.getUnderlyingValue() | + not isKnownFalsy(logical.getLeftOperand()) and + result = getAValueInput(logical.getRightOperand()) + ) + } + + private predicate preservingInput(Definition after, Definition input) { + uncertainWriteDefinitionInput(after, input) + or + phiHasInputFromBlock(after, input, _) + or + exists( + SsaConfig::SourceVariable v, js::BasicBlock bb, int i, js::VarDef write, js::Expr read, + js::BasicBlock readBlock, int readIndex + | + after.definesAt(v, bb, i) and + bb.defAt(i, v.asLocalVariable(), write) and + write.getTarget() instanceof js::VarRef and + read = getAValueInput(write.getSource()) and + read = readBlock.getNode(readIndex) and + explicitVariableRead(read, input.getSourceVariable()) and + ssaDefReachesRead(input.getSourceVariable(), input, readBlock, readIndex) + ) + } + + /** Holds if `after` can still refer to the object represented by `before`. */ + private predicate preservesDefinition(Definition before, Definition after) { + before = after + or + exists(Definition input | + preservingInput(after, input) and + preservesDefinition(before, input) + ) + } + + predicate postUpdateCfgNode(Expr read, js::Cfg::BasicBlock bb, int i) { + postUpdateOperation(read) = bb.getNode(i) and + exists( + SsaConfig::SourceVariable v, Definition before, Definition after, js::BasicBlock readBlock, + int readIndex + | + explicitVariableRead(read, v) and + read = readBlock.getNode(readIndex) and + ssaDefReachesRead(v, before, readBlock, readIndex) and + SsaConfig::variableRead(bb, i, v, true) and + ssaDefReachesRead(v, after, bb, i) and + // A later argument may have reassigned the variable to a different object. + preservesDefinition(before, after) + ) + } + + /** + * Holds if early SSA did not establish preservation and global flow should + * consult its modeled value-flow steps. + */ + additional predicate additionalPostUpdateCfgNode(Expr read, js::Cfg::BasicBlock bb, int i) { + postUpdateOperation(read) = bb.getNode(i) and + not postUpdateCfgNode(read, bb, i) and + exists(SsaConfig::SourceVariable v | + explicitVariableRead(read, v) and + ssaDefReachesRead(v, _, bb, i) + ) } cached @@ -108,3 +369,38 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig { } import DataFlowIntegration + +/** Holds if `definition` represents a conservative capture or recapture. */ +predicate isCaptureDefinition(Definition definition) { + exists(SsaConfig::SourceVariable variable, js::Cfg::BasicBlock bb, int i | + definition.definesAt(variable, bb, i) and + captureAt(bb, i, variable) + ) +} + +/** Holds if `definition` represents a value-preserving refinement. */ +predicate isRefinementDefinition(Definition definition) { + definition instanceof UncertainWriteDefinition +} + +/** Holds if `read` is an original variable or `this` read, not a continuation reference. */ +predicate isExplicitRead(SsaDataflowInput::Expr read) { explicitVariableRead(read, _) } + +/** Holds if `read` is a synthetic continuation at a potentially mutating operation. */ +predicate isPostUpdateRead(ExprNode read) { + not explicitVariableRead(read.getExpr(), read.getSourceVariable()) +} + +/** + * Holds if `continuation` follows an operation using `read`, but early SSA did + * not connect the post-update to that continuation's subsequent references. + */ +predicate additionalPostUpdateContinuation(ExprNode read, ExprNode continuation) { + explicitVariableRead(read.getExpr(), read.getSourceVariable()) and + continuation.getExpr() = postUpdateOperation(read.getExpr()) and + isPostUpdateRead(continuation) and + exists(js::Cfg::BasicBlock bb, int i | + continuation.(ReadNode).readsAt(bb, i, read.getSourceVariable()) and + SsaDataflowInput::additionalPostUpdateCfgNode(read.getExpr(), bb, i) + ) +} diff --git a/javascript/ql/src/NodeJS/InvalidExport.ql b/javascript/ql/src/NodeJS/InvalidExport.ql index 95d945bc0483..435e54484885 100644 --- a/javascript/ql/src/NodeJS/InvalidExport.ql +++ b/javascript/ql/src/NodeJS/InvalidExport.ql @@ -43,6 +43,8 @@ from Assignment assgn, Variable exportsVar, DataFlow::Node exportsVal where exportsAssign(assgn, exportsVar, exportsVal) and not exists(exportsVal.getAPredecessor()) and + // Post-updates change object contents, not the identity of the exported value. + not exportsVal instanceof DataFlow::PostUpdateNode and // this is OK if `exportsVal` flows into `module.exports` not moduleExportsAssign(_, exportsVal) and // export assignments do work in closure modules diff --git a/javascript/ql/test/library-tests/BarrierGuardPhiInputs/test.expected b/javascript/ql/test/library-tests/BarrierGuardPhiInputs/test.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/library-tests/BarrierGuardPhiInputs/test.ql b/javascript/ql/test/library-tests/BarrierGuardPhiInputs/test.ql new file mode 100644 index 000000000000..a3f51c50e36a --- /dev/null +++ b/javascript/ql/test/library-tests/BarrierGuardPhiInputs/test.ql @@ -0,0 +1,47 @@ +import javascript +import utils.test.InlineExpectationsTest + +module TestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.(DataFlow::InvokeNode).getCalleeName() = "SOURCE" + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::InvokeNode call | + call.getCalleeName() = "SINK" and + sink = call.getArgument(0) + ) + } + + predicate isBarrier(DataFlow::Node node) { + node = DataFlow::MakeBarrierGuard::getABarrierNode() + } +} + +module TestFlow = DataFlow::Global; + +class SimpleBarrierGuard extends DataFlow::InvokeNode { + SimpleBarrierGuard() { this.getCalleeName() = "BARRIER" } + + predicate blocksExpr(boolean outcome, Expr expr) { + outcome = true and + expr = this.getArgument(0).asExpr() + } +} + +module InlineTest implements TestSig { + string getARelevantTag() { result = "flow" } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "flow" and + element = "" and + exists(DataFlow::InvokeNode source, DataFlow::Node sink | + TestFlow::flow(source, sink) and + source.getCalleeName() = "SOURCE" and + value = source.getArgument(0).getStringValue() and + location = sink.getLocation() + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/BarrierGuardPhiInputs/tst.js b/javascript/ql/test/library-tests/BarrierGuardPhiInputs/tst.js new file mode 100644 index 000000000000..fe8d36bdab89 --- /dev/null +++ b/javascript/ql/test/library-tests/BarrierGuardPhiInputs/tst.js @@ -0,0 +1,35 @@ +function overwriteOnFailure() { + let x = SOURCE("overwrite"); + if (!BARRIER(x)) { + x = "safe"; + } + SINK(x); +} + +function explicitElse() { + let x = SOURCE("else"); + if (BARRIER(x)) { + use(x); + } else { + x = "safe"; + } + SINK(x); +} + +function preserveUnguardedInput() { + let x = SOURCE("guarded"); + if (!BARRIER(x)) { + x = SOURCE("fallback"); + } + SINK(x); // $ flow=fallback +} + +function returnOnFailure() { + let x = SOURCE("return"); + if (!BARRIER(x)) { + return; + } + SINK(x); +} + +SINK(SOURCE("control")); // $ flow=control diff --git a/javascript/ql/test/library-tests/CallGraphs/FullTest/tests.expected b/javascript/ql/test/library-tests/CallGraphs/FullTest/tests.expected index 1192c8ec8fb6..c43392498820 100644 --- a/javascript/ql/test/library-tests/CallGraphs/FullTest/tests.expected +++ b/javascript/ql/test/library-tests/CallGraphs/FullTest/tests.expected @@ -102,12 +102,14 @@ test_getAFunctionValue | protoclass.js:3:10:3:10 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} | | protoclass.js:4:3:4:11 | this.init | protoclass.js:7:20:11:1 | functio ... m();\\n} | | protoclass.js:7:1:7:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} | +| protoclass.js:7:1:7:11 | [ssa-synth-read] F | protoclass.js:3:1:5:1 | functio ... it();\\n} | | protoclass.js:7:1:7:16 | F.prototype.init | protoclass.js:7:20:11:1 | functio ... m();\\n} | | protoclass.js:7:1:11:1 | F.proto ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} | | protoclass.js:7:20:11:1 | functio ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} | | protoclass.js:8:3:8:13 | this.method | protoclass.js:13:22:13:34 | function() {} | | protoclass.js:9:11:9:21 | this.method | protoclass.js:13:22:13:34 | function() {} | | protoclass.js:13:1:13:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} | +| protoclass.js:13:1:13:11 | [ssa-synth-read] F | protoclass.js:3:1:5:1 | functio ... it();\\n} | | protoclass.js:13:1:13:18 | F.prototype.method | protoclass.js:13:22:13:34 | function() {} | | protoclass.js:13:1:13:34 | F.proto ... on() {} | protoclass.js:13:22:13:34 | function() {} | | protoclass.js:13:22:13:34 | function() {} | protoclass.js:13:22:13:34 | function() {} | diff --git a/javascript/ql/test/library-tests/DataFlow/tests.expected b/javascript/ql/test/library-tests/DataFlow/tests.expected index 7ba6cbb60f39..d5e5bcef5c9a 100644 --- a/javascript/ql/test/library-tests/DataFlow/tests.expected +++ b/javascript/ql/test/library-tests/DataFlow/tests.expected @@ -36,11 +36,13 @@ basicBlock | arguments.js:4:13:4:24 | alsoFirstArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:4:13:4:39 | alsoFir ... ents[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:4:28:4:36 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | +| arguments.js:4:28:4:39 | [ssa-synth-read] arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:4:28:4:39 | arguments[0] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:4:38:4:38 | 0 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:5:13:5:21 | secondArg | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:5:13:5:36 | secondA ... ents[1] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:5:25:5:33 | arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | +| arguments.js:5:25:5:36 | [ssa-synth-read] arguments | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:5:25:5:36 | arguments[1] | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:5:35:5:35 | 1 | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | | arguments.js:6:13:6:16 | args | arguments.js:2:5:2:4 | entry node of functio ... ;\\n } | @@ -171,6 +173,7 @@ basicBlock | tst2.ts:13:39:13:38 | (...arg ... rgs); } | tst2.ts:1:1:1:0 | entry node of | | tst2.ts:13:39:13:38 | ...args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | | tst2.ts:13:39:13:38 | [function self-reference] (...arg ... rgs); } | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | +| tst2.ts:13:39:13:38 | [ssa-synth-read] this | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | entry node of (...arg ... rgs); } | @@ -212,15 +215,18 @@ basicBlock | tst.js:10:1:10:1 | x | tst.js:1:1:1:0 | entry node of | | tst.js:10:1:10:4 | x, y | tst.js:1:1:1:0 | entry node of | | tst.js:10:4:10:4 | y | tst.js:1:1:1:0 | entry node of | +| tst.js:11:1:11:1 | [ssa-synth-read] y | tst.js:11:1:11:1 | guard: x is false | | tst.js:11:1:11:1 | x | tst.js:1:1:1:0 | entry node of | | tst.js:11:1:11:1 | x | tst.js:11:1:11:1 | guard: x is false | | tst.js:11:1:11:1 | x | tst.js:11:1:11:1 | guard: x is true | | tst.js:11:1:11:6 | x && y | tst.js:1:1:1:0 | entry node of | | tst.js:11:6:11:6 | y | tst.js:11:1:11:1 | guard: x is true | +| tst.js:12:1:12:1 | [ssa-synth-read] y | tst.js:12:1:12:1 | guard: x is true | | tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | guard: x is false | | tst.js:12:1:12:1 | x | tst.js:12:1:12:1 | guard: x is true | | tst.js:12:1:12:1 | x | tst.js:12:1:12:7 | x \|\| y; | | tst.js:12:1:12:6 | x \|\| y | tst.js:12:1:12:7 | x \|\| y; | +| tst.js:12:1:12:7 | [ssa-synth-read] y | tst.js:12:1:12:7 | x \|\| y; | | tst.js:12:1:12:7 | x | tst.js:12:1:12:7 | x \|\| y; | | tst.js:12:6:12:6 | y | tst.js:12:1:12:1 | guard: x is false | | tst.js:13:1:13:1 | z | tst.js:13:1:13:6 | z = y; | @@ -230,6 +236,7 @@ basicBlock | tst.js:13:5:13:5 | y | tst.js:13:1:13:6 | z = y; | | tst.js:14:1:14:1 | z | tst.js:13:1:13:6 | z = y; | | tst.js:14:1:14:9 | z ? x : y | tst.js:13:1:13:6 | z = y; | +| tst.js:14:5:14:5 | [ssa-synth-read] y | tst.js:14:1:14:1 | guard: z is true | | tst.js:14:5:14:5 | x | tst.js:14:1:14:1 | guard: z is true | | tst.js:14:9:14:9 | y | tst.js:14:1:14:1 | guard: z is false | | tst.js:16:1:20:2 | (functi ... "";\\n}) | tst.js:16:1:20:10 | (functi ... "arg"); | @@ -286,6 +293,7 @@ basicBlock | tst.js:32:12:32:12 | b | tst.js:32:1:32:0 | entry node of functio ... ables\\n} | | tst.js:33:10:33:10 | x | tst.js:32:1:32:0 | entry node of functio ... ables\\n} | | tst.js:35:1:35:1 | g | tst.js:16:1:20:10 | (functi ... "arg"); | +| tst.js:35:1:35:7 | [ssa-synth-read] g | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:35:1:35:7 | exceptional return of g(true) | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:35:1:35:7 | g(true) | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:35:3:35:6 | true | tst.js:16:1:20:10 | (functi ... "arg"); | @@ -307,10 +315,13 @@ basicBlock | tst.js:39:4:41:3 | return of method m | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } | | tst.js:40:5:40:8 | this | tst.js:39:4:39:3 | entry node of () {\\n this;\\n } | | tst.js:43:1:43:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); | +| tst.js:43:1:43:3 | [ssa-synth-read] o | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:43:1:43:3 | o.x | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:43:3:43:3 | x | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:44:1:44:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); | +| tst.js:44:1:44:3 | [ssa-synth-read] o | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:44:1:44:3 | o.m | tst.js:16:1:20:10 | (functi ... "arg"); | +| tst.js:44:1:44:5 | [ssa-synth-read] o | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:44:1:44:5 | exceptional return of o.m() | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:44:1:44:5 | o.m() | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:44:3:44:3 | m | tst.js:16:1:20:10 | (functi ... "arg"); | @@ -331,6 +342,7 @@ basicBlock | tst.js:50:14:53:3 | exceptional return of constructor of class A | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | | tst.js:50:14:53:3 | return of constructor of class A | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | | tst.js:51:5:51:9 | super | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | +| tst.js:51:5:51:13 | [ssa-synth-read] this | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | | tst.js:51:5:51:13 | exceptional return of super(42) | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | | tst.js:51:5:51:13 | super(42) | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | | tst.js:51:11:51:12 | 42 | tst.js:50:14:50:13 | entry node of () {\\n ... et`\\n } | @@ -347,6 +359,7 @@ basicBlock | tst.js:60:1:60:1 | g | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:61:1:61:5 | ::o.m | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:61:3:61:3 | o | tst.js:16:1:20:10 | (functi ... "arg"); | +| tst.js:61:3:61:5 | [ssa-synth-read] o | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:61:3:61:5 | o.m | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:61:5:61:5 | m | tst.js:16:1:20:10 | (functi ... "arg"); | | tst.js:62:1:62:1 | o | tst.js:16:1:20:10 | (functi ... "arg"); | @@ -947,8 +960,6 @@ enclosingExpr flowStep | arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:12:2 | (functi ... 3);\\n}) | | arguments.js:2:5:2:4 | arguments | arguments.js:4:28:4:36 | arguments | -| arguments.js:2:5:2:4 | arguments | arguments.js:5:25:5:33 | arguments | -| arguments.js:2:5:2:4 | arguments | arguments.js:6:20:6:28 | arguments | | arguments.js:2:5:10:5 | 'arguments' object of function f | arguments.js:4:28:4:36 | arguments | | arguments.js:2:5:10:5 | 'arguments' object of function f | arguments.js:5:25:5:33 | arguments | | arguments.js:2:5:10:5 | 'arguments' object of function f | arguments.js:6:20:6:28 | arguments | @@ -958,6 +969,12 @@ flowStep | arguments.js:2:14:2:14 | f | arguments.js:11:5:11:5 | f | | arguments.js:2:16:2:16 | x | arguments.js:2:16:2:16 | x | | arguments.js:2:16:2:16 | x | arguments.js:3:24:3:24 | x | +| arguments.js:4:28:4:36 | [post update] arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:4:28:4:36 | arguments | arguments.js:4:28:4:39 | [ssa-synth-read] arguments | +| arguments.js:4:28:4:39 | [ssa-synth-read] arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:5:25:5:33 | [post update] arguments | arguments.js:6:20:6:28 | arguments | +| arguments.js:5:25:5:33 | arguments | arguments.js:5:25:5:36 | [ssa-synth-read] arguments | +| arguments.js:5:25:5:36 | [ssa-synth-read] arguments | arguments.js:6:20:6:28 | arguments | | arguments.js:6:13:6:16 | args | arguments.js:7:24:7:27 | args | | arguments.js:6:20:6:28 | arguments | arguments.js:6:13:6:16 | args | | arguments.js:8:9:8:17 | arguments | arguments.js:9:27:9:35 | arguments | @@ -978,8 +995,8 @@ flowStep | sources.js:5:4:5:5 | 23 | sources.js:3:11:3:11 | x | | sources.js:9:14:9:18 | array | sources.js:9:14:9:18 | array | | sources.js:9:14:9:18 | array | sources.js:10:19:10:23 | array | -| sources.js:9:14:9:18 | array | sources.js:11:23:11:27 | array | | sources.js:10:12:10:14 | key | sources.js:10:28:10:30 | key | +| sources.js:10:19:10:23 | array | sources.js:11:23:11:27 | array | | sources.js:11:14:11:16 | key | sources.js:11:14:11:16 | key | | sources.js:11:14:11:16 | key | sources.js:11:32:11:34 | key | | tst2.ts:1:1:1:0 | A | tst2.ts:1:18:1:18 | A | @@ -999,12 +1016,12 @@ flowStep | tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | +| tst2.ts:13:39:13:38 | implicit 'this' | tst2.ts:13:39:13:38 | [ssa-synth-read] this | | tst2.ts:13:39:13:38 | this | tst2.ts:13:39:13:38 | implicit 'this' | | tst2.ts:15:11:15:13 | A.x | tst2.ts:15:11:15:30 | A.x satisfies number | | tst.js:1:1:1:0 | x | tst.js:3:5:3:5 | x | | tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs | | tst.js:1:10:1:11 | fs | tst.js:7:1:7:2 | fs | -| tst.js:1:10:1:11 | fs | tst.js:22:24:22:25 | fs | | tst.js:3:5:3:5 | x | tst.js:3:5:3:5 | x | | tst.js:3:5:3:5 | x | tst.js:8:1:8:1 | x | | tst.js:3:5:3:5 | x | tst.js:9:2:9:2 | x | @@ -1016,31 +1033,39 @@ flowStep | tst.js:3:5:3:5 | x | tst.js:32:1:32:0 | x | | tst.js:3:9:3:10 | 42 | tst.js:3:5:3:5 | x | | tst.js:4:5:4:5 | y | tst.js:10:4:10:4 | y | -| tst.js:4:5:4:5 | y | tst.js:11:6:11:6 | y | -| tst.js:4:5:4:5 | y | tst.js:12:6:12:6 | y | -| tst.js:4:5:4:5 | y | tst.js:13:5:13:5 | y | -| tst.js:4:5:4:5 | y | tst.js:14:9:14:9 | y | -| tst.js:4:5:4:5 | y | tst.js:105:6:105:6 | y | | tst.js:4:9:4:12 | "hi" | tst.js:4:5:4:5 | y | +| tst.js:7:1:7:2 | fs | tst.js:22:24:22:25 | fs | | tst.js:9:2:9:2 | x | tst.js:9:1:9:3 | (x) | | tst.js:10:4:10:4 | y | tst.js:10:1:10:4 | x, y | +| tst.js:10:4:10:4 | y | tst.js:11:1:11:1 | [ssa-synth-read] y | +| tst.js:10:4:10:4 | y | tst.js:11:6:11:6 | y | +| tst.js:11:1:11:1 | [ssa-synth-read] y | tst.js:12:1:12:7 | [ssa-synth-read] y | | tst.js:11:1:11:1 | x | tst.js:12:1:12:7 | x | | tst.js:11:1:11:1 | x | tst.js:12:1:12:7 | x | | tst.js:11:6:11:6 | y | tst.js:11:1:11:6 | x && y | +| tst.js:11:6:11:6 | y | tst.js:12:1:12:7 | [ssa-synth-read] y | +| tst.js:12:1:12:1 | [ssa-synth-read] y | tst.js:13:5:13:5 | y | | tst.js:12:1:12:1 | x | tst.js:12:1:12:6 | x \|\| y | | tst.js:12:1:12:1 | x | tst.js:13:1:13:6 | x | | tst.js:12:1:12:1 | x | tst.js:13:1:13:6 | x | +| tst.js:12:1:12:7 | [ssa-synth-read] y | tst.js:12:1:12:1 | [ssa-synth-read] y | +| tst.js:12:1:12:7 | [ssa-synth-read] y | tst.js:12:6:12:6 | y | | tst.js:12:1:12:7 | x | tst.js:12:1:12:1 | x | | tst.js:12:1:12:7 | x | tst.js:12:1:12:1 | x | | tst.js:12:1:12:7 | x | tst.js:12:1:12:1 | x | | tst.js:12:6:12:6 | y | tst.js:12:1:12:6 | x \|\| y | +| tst.js:12:6:12:6 | y | tst.js:13:5:13:5 | y | | tst.js:13:1:13:1 | z | tst.js:14:1:14:1 | z | | tst.js:13:1:13:6 | x | tst.js:14:5:14:5 | x | | tst.js:13:1:13:6 | x | tst.js:25:3:25:3 | x | | tst.js:13:5:13:5 | y | tst.js:13:1:13:1 | z | | tst.js:13:5:13:5 | y | tst.js:13:1:13:5 | z = y | +| tst.js:13:5:13:5 | y | tst.js:14:5:14:5 | [ssa-synth-read] y | +| tst.js:13:5:13:5 | y | tst.js:14:9:14:9 | y | +| tst.js:14:5:14:5 | [ssa-synth-read] y | tst.js:105:6:105:6 | y | | tst.js:14:5:14:5 | x | tst.js:14:1:14:9 | z ? x : y | | tst.js:14:9:14:9 | y | tst.js:14:1:14:9 | z ? x : y | +| tst.js:14:9:14:9 | y | tst.js:105:6:105:6 | y | | tst.js:16:2:20:1 | functio ... n "";\\n} | tst.js:16:1:20:2 | (functi ... "";\\n}) | | tst.js:16:13:16:13 | a | tst.js:16:13:16:13 | a | | tst.js:16:13:16:13 | a | tst.js:18:12:18:12 | a | @@ -1064,40 +1089,51 @@ flowStep | tst.js:32:1:32:0 | x | tst.js:33:10:33:10 | x | | tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:32:10:32:10 | g | | tst.js:32:10:32:10 | g | tst.js:35:1:35:1 | g | -| tst.js:32:10:32:10 | g | tst.js:60:1:60:1 | g | -| tst.js:32:10:32:10 | g | tst.js:62:4:62:4 | g | | tst.js:33:10:33:10 | x | tst.js:32:1:34:1 | return of function g | +| tst.js:35:1:35:1 | [post update] g | tst.js:60:1:60:1 | g | +| tst.js:35:1:35:1 | g | tst.js:35:1:35:7 | [ssa-synth-read] g | +| tst.js:35:1:35:7 | [ssa-synth-read] g | tst.js:60:1:60:1 | g | | tst.js:37:5:37:5 | o | tst.js:43:1:43:1 | o | -| tst.js:37:5:37:5 | o | tst.js:44:1:44:1 | o | -| tst.js:37:5:37:5 | o | tst.js:61:3:61:3 | o | -| tst.js:37:5:37:5 | o | tst.js:62:1:62:1 | o | -| tst.js:37:5:37:5 | o | tst.js:77:15:77:15 | o | -| tst.js:37:5:37:5 | o | tst.js:80:15:80:15 | o | -| tst.js:37:5:37:5 | o | tst.js:83:23:83:23 | o | -| tst.js:37:5:37:5 | o | tst.js:85:23:85:23 | o | | tst.js:37:9:42:1 | {\\n x: ... ;\\n }\\n} | tst.js:37:5:37:5 | o | | tst.js:39:4:39:3 | this | tst.js:40:5:40:8 | this | +| tst.js:43:1:43:1 | [post update] o | tst.js:44:1:44:1 | o | +| tst.js:43:1:43:1 | o | tst.js:43:1:43:3 | [ssa-synth-read] o | +| tst.js:43:1:43:3 | [ssa-synth-read] o | tst.js:44:1:44:1 | o | +| tst.js:44:1:44:1 | [post update] o | tst.js:44:1:44:5 | [ssa-synth-read] o | +| tst.js:44:1:44:1 | [post update] o | tst.js:61:3:61:3 | o | +| tst.js:44:1:44:1 | o | tst.js:44:1:44:3 | [ssa-synth-read] o | +| tst.js:44:1:44:3 | [ssa-synth-read] o | tst.js:44:1:44:5 | [ssa-synth-read] o | +| tst.js:44:1:44:5 | [ssa-synth-read] o | tst.js:61:3:61:3 | o | | tst.js:46:10:46:11 | "" | tst.js:46:1:46:11 | global = "" | | tst.js:49:1:54:1 | class A ... `\\n }\\n} | tst.js:49:7:49:7 | A | | tst.js:49:7:49:7 | A | tst.js:55:1:55:1 | A | | tst.js:50:14:50:13 | this | tst.js:51:5:51:9 | implicit 'this' | +| tst.js:51:5:51:9 | implicit 'this' | tst.js:51:5:51:13 | [ssa-synth-read] this | +| tst.js:60:1:60:1 | g | tst.js:62:4:62:4 | g | +| tst.js:61:3:61:3 | [post update] o | tst.js:62:1:62:1 | o | +| tst.js:61:3:61:3 | o | tst.js:61:3:61:5 | [ssa-synth-read] o | +| tst.js:61:3:61:5 | [ssa-synth-read] o | tst.js:62:1:62:1 | o | +| tst.js:62:1:62:1 | o | tst.js:77:15:77:15 | o | | tst.js:64:1:67:1 | functio ... lysed\\n} | tst.js:64:11:64:11 | h | | tst.js:64:11:64:11 | h | tst.js:68:12:68:12 | h | | tst.js:68:5:68:8 | iter | tst.js:69:1:69:4 | iter | | tst.js:68:12:68:14 | h() | tst.js:68:5:68:8 | iter | | tst.js:77:10:77:10 | i | tst.js:78:3:78:3 | i | +| tst.js:77:15:77:15 | o | tst.js:80:15:80:15 | o | | tst.js:80:10:80:10 | v | tst.js:81:3:81:3 | v | +| tst.js:80:15:80:15 | o | tst.js:83:23:83:23 | o | | tst.js:83:18:83:18 | v | tst.js:83:26:83:26 | v | +| tst.js:83:23:83:23 | o | tst.js:85:23:85:23 | o | | tst.js:85:18:85:18 | v | tst.js:85:26:85:26 | v | | tst.js:87:2:92:1 | functio ... + z;\\n} | tst.js:87:1:92:2 | (functi ... + z;\\n}) | | tst.js:87:13:87:16 | p: x | tst.js:87:16:87:16 | x | | tst.js:87:16:87:16 | x | tst.js:91:10:91:10 | x | | tst.js:87:22:87:22 | ...o | tst.js:87:22:87:22 | o | | tst.js:87:22:87:22 | o | tst.js:88:18:88:18 | o | -| tst.js:87:22:87:22 | o | tst.js:90:15:90:15 | o | | tst.js:88:9:88:12 | q: y | tst.js:88:12:88:12 | y | | tst.js:88:12:88:12 | y | tst.js:91:14:91:14 | y | | tst.js:88:18:88:18 | o | tst.js:88:7:88:14 | { q: y } | +| tst.js:88:18:88:18 | o | tst.js:90:15:90:15 | o | | tst.js:90:4:90:15 | { r: z } = o | tst.js:90:3:90:16 | ({ r: z } = o) | | tst.js:90:6:90:9 | r: z | tst.js:90:9:90:9 | z | | tst.js:90:9:90:9 | z | tst.js:91:18:91:18 | z | @@ -1111,10 +1147,10 @@ flowStep | tst.js:98:13:98:13 | x | tst.js:102:10:102:10 | x | | tst.js:98:19:98:22 | ...rest | tst.js:98:19:98:22 | rest | | tst.js:98:19:98:22 | rest | tst.js:99:15:99:18 | rest | -| tst.js:98:19:98:22 | rest | tst.js:101:13:101:16 | rest | | tst.js:99:9:99:9 | y | tst.js:99:9:99:9 | y | | tst.js:99:9:99:9 | y | tst.js:102:14:102:14 | y | | tst.js:99:15:99:18 | rest | tst.js:99:7:99:11 | [ y ] | +| tst.js:99:15:99:18 | rest | tst.js:101:13:101:16 | rest | | tst.js:101:7:101:7 | z | tst.js:101:7:101:7 | z | | tst.js:101:7:101:7 | z | tst.js:102:18:102:18 | z | | tst.js:101:13:101:16 | rest | tst.js:101:3:101:9 | [ , z ] | @@ -1147,7 +1183,9 @@ flowStep getImmediatePredecessor | arguments.js:1:2:12:1 | functio ... , 3);\\n} | arguments.js:1:1:12:2 | (functi ... 3);\\n}) | | arguments.js:2:5:2:4 | arguments | arguments.js:4:28:4:36 | arguments | +| arguments.js:2:5:2:4 | arguments | arguments.js:4:28:4:39 | [ssa-synth-read] arguments | | arguments.js:2:5:2:4 | arguments | arguments.js:5:25:5:33 | arguments | +| arguments.js:2:5:2:4 | arguments | arguments.js:5:25:5:36 | [ssa-synth-read] arguments | | arguments.js:2:5:2:4 | arguments | arguments.js:6:20:6:28 | arguments | | arguments.js:2:5:10:5 | functio ... ;\\n } | arguments.js:2:14:2:14 | f | | arguments.js:2:14:2:14 | f | arguments.js:11:5:11:5 | f | @@ -1189,6 +1227,7 @@ getImmediatePredecessor | tst2.ts:13:26:13:29 | List | tst2.ts:13:26:13:37 | List | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | | tst2.ts:13:39:13:38 | args | tst2.ts:13:39:13:38 | args | +| tst2.ts:13:39:13:38 | this | tst2.ts:13:39:13:38 | [ssa-synth-read] this | | tst2.ts:13:39:13:38 | this | tst2.ts:13:39:13:38 | implicit 'this' | | tst2.ts:15:11:15:13 | A.x | tst2.ts:15:11:15:30 | A.x satisfies number | | tst.js:1:10:1:11 | fs | tst.js:1:10:1:11 | fs | @@ -1237,11 +1276,16 @@ getImmediatePredecessor | tst.js:32:1:32:0 | x | tst.js:33:10:33:10 | x | | tst.js:32:1:34:1 | functio ... ables\\n} | tst.js:32:10:32:10 | g | | tst.js:32:10:32:10 | g | tst.js:35:1:35:1 | g | +| tst.js:32:10:32:10 | g | tst.js:35:1:35:7 | [ssa-synth-read] g | | tst.js:32:10:32:10 | g | tst.js:60:1:60:1 | g | | tst.js:32:10:32:10 | g | tst.js:62:4:62:4 | g | | tst.js:37:5:37:5 | o | tst.js:43:1:43:1 | o | +| tst.js:37:5:37:5 | o | tst.js:43:1:43:3 | [ssa-synth-read] o | | tst.js:37:5:37:5 | o | tst.js:44:1:44:1 | o | +| tst.js:37:5:37:5 | o | tst.js:44:1:44:3 | [ssa-synth-read] o | +| tst.js:37:5:37:5 | o | tst.js:44:1:44:5 | [ssa-synth-read] o | | tst.js:37:5:37:5 | o | tst.js:61:3:61:3 | o | +| tst.js:37:5:37:5 | o | tst.js:61:3:61:5 | [ssa-synth-read] o | | tst.js:37:5:37:5 | o | tst.js:62:1:62:1 | o | | tst.js:37:5:37:5 | o | tst.js:77:15:77:15 | o | | tst.js:37:5:37:5 | o | tst.js:80:15:80:15 | o | @@ -1253,6 +1297,7 @@ getImmediatePredecessor | tst.js:49:1:54:1 | class A ... `\\n }\\n} | tst.js:49:7:49:7 | A | | tst.js:49:7:49:7 | A | tst.js:55:1:55:1 | A | | tst.js:50:14:50:13 | this | tst.js:51:5:51:9 | implicit 'this' | +| tst.js:50:14:50:13 | this | tst.js:51:5:51:13 | [ssa-synth-read] this | | tst.js:64:1:67:1 | functio ... lysed\\n} | tst.js:64:11:64:11 | h | | tst.js:64:11:64:11 | h | tst.js:68:12:68:12 | h | | tst.js:68:5:68:8 | iter | tst.js:69:1:69:4 | iter | diff --git a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected index c6ca30cd3292..c5c9b5fe8d73 100644 --- a/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected +++ b/javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected @@ -13,6 +13,7 @@ test_getAReferenceTo | test.js:2:11:2:13 | foo | foo | | test.js:2:11:2:17 | foo.bar | foo.bar | | test.js:3:3:3:3 | v | foo.bar | +| test.js:3:3:3:7 | [ssa-synth-read] v | foo.bar | | test.js:3:3:3:7 | v.baz | foo.bar.baz | | test.js:4:7:4:24 | { baz, a, b: {c} } | foo.bar | | test.js:4:9:4:11 | baz | foo.bar.baz | diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.expected new file mode 100644 index 000000000000..6aa55dc337c7 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.expected @@ -0,0 +1,3 @@ +WARNING: type 'BarrierGuardNode' has been deprecated and may be removed in future (captured.ql:32,40-66) +WARNING: type 'BarrierGuardNode' has been deprecated and may be removed in future (captured.ql:43,37-63) +WARNING: type 'Configuration' has been deprecated and may be removed in future (captured.ql:36,36-59) diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.js b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.js new file mode 100644 index 000000000000..d83e90bc64d5 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.js @@ -0,0 +1,114 @@ +import "dummy"; + +let capturedWrite = 0; // $ capturedWrite=capturedWrite ssaDefinition=capturedWrite noSsaAstNode=capturedWrite + +function readCaptured() { + return capturedWrite; // $ noCapturedWrite=capturedWrite +} + +function unreachableCapturedWrites() { + return; + capturedWrite = 2; // $ noCapturedWrite=capturedWrite +} + +function writeCaptured() { + capturedWrite = 1; // $ capturedWrite=capturedWrite ssaDefinition=capturedWrite + ++capturedWrite; // $ capturedWrite=capturedWrite ssaDefinition=capturedWrite + capturedWrite += 1; // $ capturedWrite=capturedWrite ssaDefinition=capturedWrite +} + +function uncapturedWrites() { + let uncapturedWrite = 0; // $ noCapturedWrite=uncapturedWrite + return uncapturedWrite++; // $ noCapturedWrite=uncapturedWrite +} + +globalWrite = 1; // $ noCapturedWrite=globalWrite +use(globalWrite); // $ noCapturedWrite=globalWrite + +const { capturedPattern } = input; // $ capturedWrite=capturedPattern ssaDefinition=capturedPattern +function readCapturedPattern() { + return capturedPattern; // $ noCapturedWrite=capturedPattern +} + +function destructuredDeclaration() { + const { declaredObject, safeObject } = { // $ ssaDefinition=declaredObject ssaDefinition=safeObject + declaredObject: SOURCE("object-declaration"), + safeObject: "safe" + }; + SINK(declaredObject); // $ localCapturedFlow=declaredObject capturedFlow=object-declaration legacyCapturedFlow=object-declaration + SINK(safeObject); // $ localCapturedFlow=safeObject + return () => { + SINK(declaredObject); // $ capturedFlow=object-declaration legacyCapturedFlow=object-declaration + SINK(safeObject); + }; +} +destructuredDeclaration()(); + +function destructuredAssignment() { + let assignedObject; + ({ assignedObject } = { assignedObject: SOURCE("object-assignment") }); // $ ssaDefinition=assignedObject + SINK(assignedObject); // $ localCapturedFlow=assignedObject capturedFlow=object-assignment legacyCapturedFlow=object-assignment + return () => SINK(assignedObject); // $ capturedFlow=object-assignment legacyCapturedFlow=object-assignment +} +destructuredAssignment()(); + +function destructuredParameter({ parameterObject }) { // $ ssaDefinition=parameterObject + SINK(parameterObject); // $ localCapturedFlow=parameterObject capturedFlow=object-parameter legacyCapturedFlow=object-parameter + return () => SINK(parameterObject); // $ capturedFlow=object-parameter legacyCapturedFlow=object-parameter +} +destructuredParameter({ parameterObject: SOURCE("object-parameter") })(); + +function destructuredLoop() { + for (const { loopObject } of [{ loopObject: SOURCE("object-loop") }]) { // $ ssaDefinition=loopObject + SINK(loopObject); // $ localCapturedFlow=loopObject capturedFlow=object-loop legacyCapturedFlow=object-loop + (() => SINK(loopObject))(); // $ capturedFlow=object-loop legacyCapturedFlow=object-loop + } +} +destructuredLoop(); + +function destructuredArray([arrayElement]) { // $ ssaDefinition=arrayElement + SINK(arrayElement); // $ localCapturedFlow=arrayElement capturedFlow=array-element legacyCapturedFlow=array-element + return () => SINK(arrayElement); // $ capturedFlow=array-element legacyCapturedFlow=array-element +} +destructuredArray([SOURCE("array-element")])(); + +const { topLevelObject } = { topLevelObject: SOURCE("top-level-object") }; // $ ssaDefinition=topLevelObject +function readTopLevelObject() { + SINK(topLevelObject); // $ capturedFlow=top-level-object legacyCapturedFlow=top-level-object +} +readTopLevelObject(); + +let topLevelPhi; +if (unknownCondition()) { + topLevelPhi = SOURCE("top-level-phi"); // $ ssaDefinition=topLevelPhi +} else { + topLevelPhi = "safe"; // $ ssaDefinition=topLevelPhi +} +SINK(topLevelPhi); // $ capturedFlow=top-level-phi legacyCapturedFlow=top-level-phi +function readTopLevelPhi() { + SINK(topLevelPhi); // $ capturedFlow=top-level-phi legacyCapturedFlow=top-level-phi +} +readTopLevelPhi(); + +function legacyGuardedPhi(condition) { + let value = SOURCE("legacy-guarded-phi"); + if (condition && BARRIER(value)) { + } else { + value = "safe"; + } + SINK(value); +} + +function legacyGuardedWithReads(condition) { + let value = SOURCE("legacy-with-guard"); + use(value); + with ({}) { + if (condition) { + if (!BARRIER(value)) return; + use(value); + } else { + value = "safe"; + } + } + SINK(value); +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.ql new file mode 100644 index 000000000000..7cebe78259ac --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/captured.ql @@ -0,0 +1,128 @@ +import javascript +import utils.test.InlineExpectationsTest + +module CapturedConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.(DataFlow::InvokeNode).getCalleeName() = "SOURCE" + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::InvokeNode call | + call.getCalleeName() = "SINK" and + sink = call.getArgument(0) + ) + } + + predicate isBarrier(DataFlow::Node node) { + node = DataFlow::MakeBarrierGuard::getABarrierNode() + } +} + +module CapturedFlow = DataFlow::Global; + +class SimpleBarrierGuard extends DataFlow::InvokeNode { + SimpleBarrierGuard() { this.getCalleeName() = "BARRIER" } + + predicate blocksExpr(boolean outcome, Expr expr) { + outcome = true and + expr = this.getArgument(0).asExpr() + } +} + +class LegacySimpleBarrierGuard extends DataFlow::BarrierGuardNode, SimpleBarrierGuard { + override predicate blocks(boolean outcome, Expr expr) { this.blocksExpr(outcome, expr) } +} + +class LegacyCapturedConfig extends DataFlow::Configuration { + LegacyCapturedConfig() { this = "LegacyCapturedConfig" } + + override predicate isSource(DataFlow::Node source) { CapturedConfig::isSource(source) } + + override predicate isSink(DataFlow::Node sink) { CapturedConfig::isSink(sink) } + + override predicate isBarrierGuard(DataFlow::BarrierGuardNode guard) { + guard instanceof LegacySimpleBarrierGuard + } +} + +module InlineTest implements TestSig { + string getARelevantTag() { + result = + [ + "capturedWrite", "noCapturedWrite", "ssaDefinition", "noSsaAstNode", "localCapturedFlow", + "capturedFlow", "legacyCapturedFlow" + ] + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + element = "" and + location.getFile().getBaseName() = "captured.js" and + ( + exists(VarRef reference | + value = reference.getVariable().getName() and + value = ["capturedWrite", "uncapturedWrite", "globalWrite", "capturedPattern"] and + location = reference.getLocation() and + ( + tag = "capturedWrite" and + exists(DataFlow::SsaDefinitionNode node | + node.getSsaVariable().getDefinition().(SsaExplicitDefinition).getLhs() = reference and + reference.getVariable().isCaptured() + ) + or + tag = "noCapturedWrite" and + not exists(DataFlow::SsaDefinitionNode node | + node.getSsaVariable().getDefinition().(SsaExplicitDefinition).getLhs() = reference and + reference.getVariable().isCaptured() + ) + ) + ) + or + tag = "ssaDefinition" and + exists(SsaExplicitDefinition definition, VarRef write, DataFlow::SsaDefinitionNode node | + write = definition.getLhs() and + write.getVariable().isCaptured() and + node = DataFlow::lvalueNode(write) and + node = DataFlow::ssaDefinitionNode(definition) and + node.getSsaVariable() = definition.getVariable() and + location = write.getLocation() and + value = write.getVariable().getName() + ) + or + tag = "noSsaAstNode" and + exists(VarDecl write | + write.getVariable().getName() = "capturedWrite" and + not exists(DataFlow::lvalueNode(write).getAstNode()) and + location = write.getLocation() and + value = write.getVariable().getName() + ) + or + tag = "localCapturedFlow" and + exists(DataFlow::InvokeNode sink, VarUse read, SsaExplicitDefinition definition | + sink.getCalleeName() = "SINK" and + sink.getArgument(0).asExpr() = read and + read.getSsaVariable().getDefinition() = definition and + definition.getSourceVariable().isCaptured() and + definition.getContainer() = sink.getContainer() and + DataFlow::lvalueNode(definition.getLhs()).getASuccessor+() = sink.getArgument(0) and + location = sink.getLocation() and + value = read.getVariable().getName() + ) + or + exists(DataFlow::InvokeNode source, DataFlow::InvokeNode sink | + source.getFile().getBaseName() = "captured.js" and + source.getCalleeName() = "SOURCE" and + sink.getCalleeName() = "SINK" and + value = source.getArgument(0).getStringValue() and + location = sink.getLocation() and + ( + tag = "capturedFlow" and CapturedFlow::flow(source, sink.getArgument(0)) + or + tag = "legacyCapturedFlow" and + any(LegacyCapturedConfig config).hasFlow(source, sink.getArgument(0)) + ) + ) + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.expected new file mode 100644 index 000000000000..1cbd6fd3eedf --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.expected @@ -0,0 +1,21 @@ +nonUniqueDef +readWithoutDef +deadDef +notDominatedByDef +nonUniqueDefReachesEndOfBlock +uselessPhiNode +readWithoutPriorRef +readWithMultiplePriorRefs +phiWithoutTwoPriorRefs +phiReadWithoutTwoPriorRefs +ambiguousReadNode +nonUniqueSsaDefinition +nonUniqueSsaVariable +missingSsaNodeMetadata +orphanSsaDefinitionNode +ssaDefinitionHasAstNode +unmappedWriteDefSourceNode +nonUniqueSharedSsaNodeMapping +sharedReadContainerMismatch +postUpdateMustFlow +localFlowIsLocal diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.ql new file mode 100644 index 000000000000..74077540309d --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/invariants.ql @@ -0,0 +1,61 @@ +import javascript +import semmle.javascript.dataflow.internal.DataFlowNode +import semmle.javascript.dataflow.internal.DataFlowImplConsistency as DataFlowImplConsistency +import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2 +import Ssa2::Consistency +import Ssa2::DfConsistency + +query predicate localFlowIsLocal = DataFlowImplConsistency::Consistency::localFlowIsLocal/3; + +query predicate nonUniqueSsaDefinition(SsaDefinition definition, int nodes) { + nodes = count(DataFlow::ssaDefinitionNode(definition)) and + nodes != 1 +} + +query predicate nonUniqueSsaVariable(DataFlow::SsaDefinitionNode node, int variables) { + variables = count(node.getSsaVariable()) and + variables != 1 +} + +query predicate missingSsaNodeMetadata(DataFlow::Node node) { + ( + node instanceof TSharedSsaDefNode or + node instanceof TSharedSsaReadNode or + node instanceof TSsaSynthReadNode + ) and + not ( + exists(node.toString()) and + (exists(node.getBasicBlock()) or node instanceof TSharedSsaReadNode) and + exists(node.getContainer()) and + exists(node.getLocation()) and + exists(node.getFile()) + ) +} + +query predicate orphanSsaDefinitionNode(DataFlow::Node node) { + node instanceof TSharedSsaDefNode and + not node instanceof DataFlow::SsaDefinitionNode +} + +query predicate ssaDefinitionHasAstNode(DataFlow::SsaDefinitionNode node, AstNode ast) { + ast = node.getAstNode() +} + +query predicate unmappedWriteDefSourceNode(Ssa2::WriteDefSourceNode node, int mapped) { + mapped = count(DataFlow::getNodeFromSharedSsa(node)) and + mapped != 1 +} + +query predicate nonUniqueSharedSsaNodeMapping(Ssa2::Node node, int mapped) { + mapped = count(DataFlow::getNodeFromSharedSsa(node)) and + mapped > 1 +} + +query predicate sharedReadContainerMismatch(Ssa2::ExprNode read) { + TSharedSsaReadNode(read).(DataFlow::Node).getContainer() != + DataFlow::getNodeFromSharedSsa(read).(DataFlow::Node).getContainer() +} + +query predicate postUpdateMustFlow(DataFlow::PostUpdateNode update, DataFlow::Node node) { + node.getImmediatePredecessor() = update +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/local.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/local.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/local.js b/javascript/ql/test/library-tests/SharedSsaCompatibility/local.js new file mode 100644 index 000000000000..1b75d67293a1 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/local.js @@ -0,0 +1,192 @@ +function ordinaryPhiLocalFlow(condition, otherCondition) { + let value; + if (condition) { + value = SOURCE("ordinary-phi"); + } else { + value = {}; + } + if (otherCondition) { + SINK(value); // $ localFlow=ordinary-phi + } else { + use(value); + } +} + +function postUpdateOrdering(value) { + SINK(value); // $ noPostUpdateFlow=post-update + value.property = SOURCE("post-update"); + SINK(value); // $ postUpdateFlow=post-update +} + +function assignmentUpdateCompletion(value) { + value.property = SOURCE("store-completion", SINK(value)); // $ noPostUpdateFlow=store-completion noGlobalPostUpdateFlow=store-completion + SINK(value); // $ postUpdateFlow=store-completion globalPostUpdateFlow=store-completion +} + +function callUpdateCompletion(value) { + UPDATE("call-completion", value, SINK(value)); // $ noPostUpdateFlow=call-completion noGlobalPostUpdateFlow=call-completion + SINK(value); // $ postUpdateFlow=call-completion globalPostUpdateFlow=call-completion +} + +function nestedCallUpdateCompletion(value) { + use(UPDATE("nested-call-completion", value), SINK(value)); // $ postUpdateFlow=nested-call-completion globalPostUpdateFlow=nested-call-completion +} + +function overwrittenCallArgument(value) { + UPDATE("overwritten-call-argument", value, value = {}); + SINK(value); // $ noPostUpdateFlow=overwritten-call-argument noGlobalPostUpdateFlow=overwritten-call-argument +} + +function selfAssignedCallUpdate(value) { + UPDATE("self-assigned-call-update", value, value = value); + SINK(value); // $ postUpdateFlow=self-assigned-call-update globalPostUpdateFlow=self-assigned-call-update +} + +function selfAssignedStoreUpdate(value) { + value.property = (value = value, SOURCE("self-assigned-store-update")); + SINK(value); // $ postUpdateFlow=self-assigned-store-update globalPostUpdateFlow=self-assigned-store-update +} + +function conditionalCallUpdate(value, condition) { + UPDATE("conditional-call-update", value, value = condition ? value : {}); + SINK(value); // $ postUpdateFlow=conditional-call-update globalPostUpdateFlow=conditional-call-update +} + +function unusedOverwrittenCallArgument(value) { + UPDATE("unused-overwritten-call-argument", value, value = {}); +} + +function refinedCallArgument(value) { + UPDATE("refined-call-argument", value, typeof value === "object" ? 1 : 2); + SINK(value); // $ postUpdateFlow=refined-call-argument globalPostUpdateFlow=refined-call-argument +} + +function computedStoreCompletion(value) { + value[ + SINK(value) // $ noPostUpdateFlow=computed-store-completion noGlobalPostUpdateFlow=computed-store-completion + ] = SOURCE( + "computed-store-completion", + SINK(value) // $ noPostUpdateFlow=computed-store-completion noGlobalPostUpdateFlow=computed-store-completion + ); + SINK(value); // $ postUpdateFlow=computed-store-completion globalPostUpdateFlow=computed-store-completion +} + +function destructuringStoreCompletion(value) { + [value.first, value[SINK(value)]] = SOURCE("destructuring-store-completion"); // $ postUpdateFlow=destructuring-store-completion globalPostUpdateFlow=destructuring-store-completion + SINK(value); // $ postUpdateFlow=destructuring-store-completion globalPostUpdateFlow=destructuring-store-completion +} + +function exceptionalCallUpdate(value) { + try { + UPDATE("exceptional-call-completion", value); + } catch (error) { + SINK(value); // $ postUpdateFlow=exceptional-call-completion globalPostUpdateFlow=exceptional-call-completion + } + SINK(value); // $ postUpdateFlow=exceptional-call-completion globalPostUpdateFlow=exceptional-call-completion +} + +function loopUpdateCompletion(value) { + while (condition()) { + SINK(value); // $ postUpdateFlow=loop-completion globalPostUpdateFlow=loop-completion + UPDATE("loop-completion", value); + SINK(value); // $ postUpdateFlow=loop-completion globalPostUpdateFlow=loop-completion + } +} + +function reflectiveReadInference() { + const value = () => 42; + CHECK_TYPE(value); // $ noReflectiveValue + with ({}) { + CHECK_TYPE(value); // $ reflectiveValue + } + CHECK_TYPE(value); // $ noReflectiveValue +} + +function nestedRefinementPostUpdate(value) { + if (value) { + if (typeof value === "object") { + value.property = SOURCE("nested-post-update"); + SINK(value); // $ postUpdateFlow=nested-post-update + } + } +} + +function refinedPostUpdateReads(value) { + if (value) { + SINK(value); // $ noPostUpdateFlow=refined-post-update + value.property = SOURCE("refined-post-update"); + use(value); + SINK(value); // $ postUpdateFlow=refined-post-update + } +} + +function compatibilityPhiPostUpdateReads(value) { + if (typeof value === "object" || typeof value === "function") { + SINK(value); // $ noPostUpdateFlow=compatibility-phi-post-update + value.property = SOURCE("compatibility-phi-post-update"); + use(value); + SINK(value); // $ postUpdateFlow=compatibility-phi-post-update + } +} + +function postUpdateBeforeNestedRefinement(value) { + if (value) { + value.property = SOURCE("update-before-refinement"); + if (typeof value === "object") { + SINK(value); // $ postUpdateFlow=update-before-refinement + } + } +} + +function postUpdateBeforeCompatibilityPhi(value) { + if (value) { + value.property = SOURCE("update-before-phi"); + use(value); + } + SINK(value); // $ postUpdateFlow=update-before-phi +} + +function postUpdateImmediatePredecessor(value) { + value.property = SOURCE("update-immediate-predecessor"); + SINK(value); // $ postUpdateFlow=update-immediate-predecessor noImmediatePostUpdate=update-immediate-predecessor +} + +function capturedRefinementLocalFlow() { + const value = SOURCE("captured-refinement"); + function capture() { + use(value); + } + if (value) { + if (typeof value === "object") { + SINK(value); // $ localFlow=captured-refinement + } + } +} + +function parameterReadBeforeDeclaration(first = second, second = SOURCE("later-parameter")) { + SINK(first); + SINK(second); // $ localFlow=later-parameter +} + +function unreachableLocalSsaCode() { + const liveBeforeReturn = "safe"; + return liveBeforeReturn; + const deadLocal = "dead"; + use(deadLocal); + use(liveBeforeReturn); +} + +class SharedSsaBase {} + +class SharedSsaDerived extends SharedSsaBase { + field = this.condition ? 1 : 2; + + constructor(condition) { + if (condition) { + super(); // $ noSuperFlow noThisSelfFlow + } else { + super(); // $ noSuperFlow noThisSelfFlow + } + use(this); + } +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/local.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/local.ql new file mode 100644 index 000000000000..9e8da0b33f03 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/local.ql @@ -0,0 +1,160 @@ +import javascript +import semmle.javascript.dataflow.internal.DataFlowNode +import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2 +import utils.test.InlineExpectationsTest + +private DataFlow::PostUpdateNode postUpdateForSource(string value) { + exists(Assignment assignment, PropAccess property, DataFlow::InvokeNode source | + ( + assignment.getTarget() = property + or + assignment.getTarget().(ArrayPattern).getElement(0) = property + ) and + assignment.getRhs().getUnderlyingValue() = source.asExpr() and + source.getCalleeName() = "SOURCE" and + source.getArgument(0).getStringValue() = value and + result.getPreUpdateNode() = DataFlow::valueNode(property.getBase()) + ) + or + exists(DataFlow::InvokeNode call | + call.getCalleeName() = "UPDATE" and + call.getArgument(0).getStringValue() = value and + result.getPreUpdateNode() = call.getArgument(1) + ) +} + +module PostUpdateConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { source = postUpdateForSource(_) } + + predicate isSink(DataFlow::Node sink) { + sink = any(DataFlow::InvokeNode call | call.getCalleeName() = "SINK").getArgument(0) + } +} + +module PostUpdateFlow = DataFlow::Global; + +module InlineTest implements TestSig { + string getARelevantTag() { + result = + [ + "localFlow", "postUpdateFlow", "noPostUpdateFlow", "noImmediatePostUpdate", "noSuperFlow", + "noThisSelfFlow", "superPostUpdateToThisFlow", "fieldReadContainerMismatch", + "globalPostUpdateFlow", "noGlobalPostUpdateFlow", "reflectiveValue", "noReflectiveValue" + ] + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + element = "" and + location.getFile().getBaseName() = "local.js" and + ( + tag = "localFlow" and + exists(DataFlow::InvokeNode source, DataFlow::InvokeNode sink | + source.getCalleeName() = "SOURCE" and + value = source.getArgument(0).getStringValue() and + sink.getCalleeName() = "SINK" and + source.getContainer() = sink.getContainer() and + source.flowsTo(sink.getArgument(0)) and + location = sink.getLocation() + ) + or + tag = "postUpdateFlow" and + exists(DataFlow::InvokeNode sink, DataFlow::PostUpdateNode update | + sink.getCalleeName() = "SINK" and + update = postUpdateForSource(value) and + update.getContainer() = sink.getContainer() and + update.getASuccessor+() = sink.getArgument(0) and + location = sink.getLocation() + ) + or + tag = "noPostUpdateFlow" and + exists(DataFlow::InvokeNode sink, DataFlow::PostUpdateNode update | + sink.getCalleeName() = "SINK" and + update = postUpdateForSource(value) and + update.getContainer() = sink.getContainer() and + not update.getASuccessor+() = sink.getArgument(0) and + location = sink.getLocation() + ) + or + exists(DataFlow::InvokeNode sink, DataFlow::PostUpdateNode update | + value = + [ + "store-completion", "call-completion", "nested-call-completion", + "overwritten-call-argument", "loop-completion", "refined-call-argument", + "computed-store-completion", "exceptional-call-completion", + "destructuring-store-completion", "self-assigned-call-update", + "self-assigned-store-update", "conditional-call-update" + ] and + sink.getCalleeName() = "SINK" and + update = postUpdateForSource(value) and + update.getContainer() = sink.getContainer() and + location = sink.getLocation() and + ( + tag = "globalPostUpdateFlow" and PostUpdateFlow::flow(update, sink.getArgument(0)) + or + tag = "noGlobalPostUpdateFlow" and not PostUpdateFlow::flow(update, sink.getArgument(0)) + ) + ) + or + exists(DataFlow::InvokeNode call | + call.getCalleeName() = "CHECK_TYPE" and + location = call.getLocation() and + value = "" and + ( + tag = "reflectiveValue" and + call.getArgument(0).analyze().getALocalValue().isIndefinite("eval") + or + tag = "noReflectiveValue" and + not call.getArgument(0).analyze().getALocalValue().isIndefinite("eval") + ) + ) + or + tag = "noImmediatePostUpdate" and + value = "update-immediate-predecessor" and + exists(DataFlow::InvokeNode sink, DataFlow::PostUpdateNode update | + sink.getCalleeName() = "SINK" and + update = postUpdateForSource(value) and + update.getContainer() = sink.getContainer() and + not sink.getArgument(0).getImmediatePredecessor() = update and + location = sink.getLocation() + ) + or + tag = "noSuperFlow" and + value = "" and + exists(SuperCall call, Function constructor | + constructor = call.getEnclosingFunction() and + not DataFlow::thisNode(constructor).flowsTo(DataFlow::valueNode(call.getCallee())) and + location = call.getLocation() + ) + or + tag = "noThisSelfFlow" and + value = "" and + exists(SuperCall call, Function constructor, DataFlow::ThisNode thisNode | + constructor = call.getEnclosingFunction() and + thisNode = DataFlow::thisNode(constructor) and + not thisNode.getASuccessor() = thisNode and + location = call.getLocation() + ) + or + tag = "superPostUpdateToThisFlow" and + value = "" and + exists(SuperCall call, Function constructor, ThisExpr thiz | + constructor = call.getEnclosingFunction() and + thiz.getBinder() = constructor and + DataFlow::valueNode(call.getCallee()).getPostUpdateNode().getASuccessor+() = + DataFlow::valueNode(thiz) and + location = call.getLocation() + ) + or + tag = "fieldReadContainerMismatch" and + value = "" and + exists(FieldDefinition field, Ssa2::ExprNode read, DataFlow::Node node | + read.getExpr() = field and + node = TSharedSsaReadNode(read) and + node.getContainer() != field.getDeclaringClass().getConstructor().getBody() and + location = field.getLocation() + ) + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/namespace.ts b/javascript/ql/test/library-tests/SharedSsaCompatibility/namespace.ts new file mode 100644 index 000000000000..c5f28576d9ff --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/namespace.ts @@ -0,0 +1,9 @@ +namespace SharedSsaNamespace { + let value: unknown; + if (check()) { + value = SOURCE("ts-namespace"); + } + if (value) { + SINK(value); // $ localRefinementFlow=ts-namespace noRefinementBypass=ts-namespace refinementFlow=ts-namespace refinementTaint=ts-namespace + } +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.js b/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.js new file mode 100644 index 000000000000..ce456e295c4a --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.js @@ -0,0 +1,88 @@ +function ordinaryPhiNodes(condition, otherCondition) { + let value; + if (condition) { + value = "first"; + } else { + value = "second"; + } + if (otherCondition) { + CHECK(value); // $ uniquePhiDefinition phiNodeMetadata phiReachesCheck globalPhiReachesCheck + } else { + use(value); + } +} + +function refinedPhiNodes(value, condition) { + if (value) { + value = "safe"; + } + if (condition) { + CHECK(value); // $ uniquePhiDefinition phiNodeMetadata phiReachesCheck globalPhiReachesCheck + } else { + use(value); + } +} + +function nestedPhiNodes(firstCondition, secondCondition, thirdCondition) { + let value; + if (firstCondition) { + if (secondCondition) { + value = "first"; + } else { + value = "second"; + } + otherOperation(); + } else { + value = "third"; + } + if (thirdCondition) { + CHECK(value); // $ uniquePhiDefinition phiNodeMetadata phiReachesCheck globalPhiReachesCheck + } else { + use(value); + } +} + +function capturedPhiNodes(condition, otherCondition) { + let value; + if (condition) { + value = "first"; + } else { + value = "second"; + } + function capture() { + return value; + } + capture(); + if (otherCondition) { + CHECK(value); // $ uniquePhiDefinition phiNodeMetadata phiReachesCheck globalPhiReachesCheck + } else { + use(value); + } +} + +function sharedDefinitionApi(parameter) { + CHECK_NATIVE(parameter); // $ sharedDefinition + let uninitialized; + CHECK_NATIVE(uninitialized); // $ sharedDefinition + const { value } = { value: parameter }; + CHECK_NATIVE(value); // $ sharedDefinition + if (typeof value === "object") { + CHECK_NATIVE(value); // $ sharedDefinition + } +} + +function capturedDefinitionApi() { + let value = 0; + function write() { + value = 1; + value = 2; + } + function* read() { + CHECK_NATIVE(value); // $ sharedDefinition + yield; + CHECK_NATIVE(value); // $ sharedDefinition + } + write(); + CHECK_NATIVE(value); // $ sharedDefinition + return read; +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.ql new file mode 100644 index 000000000000..f936e7f0209b --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/nodes.ql @@ -0,0 +1,84 @@ +import javascript +import semmle.javascript.dataflow.internal.sharedlib.Ssa as Ssa2 +import utils.test.InlineExpectationsTest + +module PhiConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.(DataFlow::SsaDefinitionNode).getSsaVariable().getDefinition() instanceof SsaPhiNode + } + + predicate isSink(DataFlow::Node sink) { + sink = any(DataFlow::InvokeNode check | check.getCalleeName() = "CHECK").getArgument(0) + } +} + +module PhiFlow = DataFlow::Global; + +module InlineTest implements TestSig { + string getARelevantTag() { + result = + [ + "uniquePhiDefinition", "phiNodeMetadata", "phiReachesCheck", "globalPhiReachesCheck", + "sharedDefinition" + ] + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + element = "" and + value = "" and + location.getFile().getBaseName() = "nodes.js" and + ( + tag = "sharedDefinition" and + exists( + DataFlow::InvokeNode check, VarUse read, Ssa2::Definition definition, + Ssa2::SsaConfig::SourceVariable variable, BasicBlock bb, int index + | + check.getCalleeName() = "CHECK_NATIVE" and + read = check.getArgument(0).asExpr() and + read = bb.getNode(index) and + variable.asLocalVariable() = read.getVariable() and + Ssa2::ssaDefReachesRead(variable, definition, bb, index) and + read.getSsaVariable().getDefinition() = definition and + location = check.getLocation() + ) + or + exists(DataFlow::InvokeNode check, VarUse read, SsaPhiNode phi | + check.getCalleeName() = "CHECK" and + check.getArgument(0).asExpr() = read and + read.getSsaVariable().getDefinition() = phi and + location = check.getLocation() and + ( + tag = "uniquePhiDefinition" and + count(DataFlow::ssaDefinitionNode(phi)) = 1 + or + tag = "phiNodeMetadata" and + forall(DataFlow::Node node | node = DataFlow::ssaDefinitionNode(phi) | + exists(node.toString()) and + exists(node.getBasicBlock()) and + exists(node.getContainer()) and + exists(node.getLocation()) and + exists(node.getFile()) + ) + or + tag = "phiReachesCheck" and + forall(SsaPhiNode input | + input.getSourceVariable() = read.getVariable() and + input.getContainer() = check.getContainer() + | + DataFlow::ssaDefinitionNode(input).getASuccessor+() = check.getArgument(0) + ) + or + tag = "globalPhiReachesCheck" and + forall(SsaPhiNode input | + input.getSourceVariable() = read.getVariable() and + input.getContainer() = check.getContainer() + | + PhiFlow::flow(DataFlow::ssaDefinitionNode(input), check.getArgument(0)) + ) + ) + ) + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.js b/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.js new file mode 100644 index 000000000000..6ebc757ea2d4 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.js @@ -0,0 +1,493 @@ +function refinementBarrier() { + const refinementBarrierSource = SOURCE("refinement-barrier"); + if (!refinementBarrierSource) { + SINK(refinementBarrierSource); // $ barrier=refinement-barrier noRefinementFlow=refinement-barrier noRefinementTaint=refinement-barrier localRefinementFlow=refinement-barrier noRefinementBypass=refinement-barrier + } +} + +function propertyGuardDoesNotSanitizeBase(key) { + const object = SOURCE("guarded-property-base"); + if (!BARRIER(object[key])) return; + SINK(object[key]); // $ noRefinementFlow=guarded-property-base noRefinementTaint=guarded-property-base + SINK(object); // $ refinementFlow=guarded-property-base refinementTaint=guarded-property-base localRefinementFlow=guarded-property-base + key = OTHER_KEY(); + SINK(object[key]); // $ noRefinementFlow=guarded-property-base refinementTaint=guarded-property-base +} + +function phiReadInsideRefinement(condition) { + const phiReadSource = SOURCE("refined-phi-read"); + if (phiReadSource) { + if (condition) { + if (!BARRIER(phiReadSource)) return; + } else { + if (!BARRIER(phiReadSource)) return; + } + SINK(phiReadSource); // $ noRefinementFlow=refined-phi-read noRefinementTaint=refined-phi-read localRefinementFlow=refined-phi-read noRefinementBypass=refined-phi-read + } +} + +function refinementFollowedByPhi(condition) { + let refinementPhiSource = SOURCE("refinement-phi"); + if (!refinementPhiSource) { + if (condition) { + refinementPhiSource = "safe"; + } + SINK(refinementPhiSource); // $ noRefinementFlow=refinement-phi noRefinementTaint=refinement-phi localRefinementFlow=refinement-phi noRefinementBypass=refinement-phi + } +} + +function equalityCompatibilityPhi() { + const value = SOURCE("equality-phi"); + if (value === "a" || value === "b") { + SINK(value); // $ refinementFlow=equality-phi noRefinementTaint=equality-phi localRefinementFlow=equality-phi noRefinementBypass=equality-phi + } +} + +function nestedCompatibilityPhi(condition) { + const value = SOURCE("nested-equality-phi"); + if (value === "a" || value === "b") { + if (condition) { + use(value); + } else { + use(value); + } + SINK(value); // $ refinementFlow=nested-equality-phi noRefinementTaint=nested-equality-phi localRefinementFlow=nested-equality-phi noRefinementBypass=nested-equality-phi + } +} + +function unguardedWriteAfterTruthyTest(value) { + if (value) { + value = SOURCE("unguarded-shared-phi"); + } + SINK(value); // $ refinementFlow=unguarded-shared-phi refinementTaint=unguarded-shared-phi localRefinementFlow=unguarded-shared-phi +} + +function unguardedWriteInsideFalseRefinement(value, condition) { + if (!value) { + if (condition) { + value = SOURCE("nested-shared-phi"); + } + SINK(value); // $ refinementFlow=nested-shared-phi refinementTaint=nested-shared-phi localRefinementFlow=nested-shared-phi + } +} + +function transitiveSharedPhis(value, condition) { + if (value) { + value = SOURCE("first-shared-phi"); + } + if (condition) { + value = SOURCE("second-shared-phi"); + } + SINK(value); // $ refinementFlow=first-shared-phi refinementTaint=first-shared-phi localRefinementFlow=first-shared-phi refinementFlow=second-shared-phi refinementTaint=second-shared-phi localRefinementFlow=second-shared-phi +} + +function phiReadAfterSharedPhi(value, condition) { + if (value) { + value = SOURCE("shared-phi-then-read"); + } + if (condition) { + use(value); + } else { + use(value); + } + SINK(value); // $ refinementFlow=shared-phi-then-read refinementTaint=shared-phi-then-read localRefinementFlow=shared-phi-then-read +} + +function filteredPhiInputWithReplacement(condition, otherCondition) { + let value = SOURCE("filtered-phi-input"); + if (!value) { + if (condition) { + value = SOURCE("replacement-phi-input"); + } + if (otherCondition) { + SINK(value); // $ noRefinementFlow=filtered-phi-input noRefinementTaint=filtered-phi-input localRefinementFlow=filtered-phi-input noRefinementBypass=filtered-phi-input refinementFlow=replacement-phi-input refinementTaint=replacement-phi-input localRefinementFlow=replacement-phi-input + } else { + SINK(value); // $ noRefinementFlow=filtered-phi-input noRefinementTaint=filtered-phi-input localRefinementFlow=filtered-phi-input noRefinementBypass=filtered-phi-input refinementFlow=replacement-phi-input refinementTaint=replacement-phi-input localRefinementFlow=replacement-phi-input + } + } + + function emptyRefinementBranches() { + const value = SOURCE("empty-refinement-branches"); + if (value) {} + SINK(value); // $ refinementFlow=empty-refinement-branches refinementTaint=empty-refinement-branches localRefinementFlow=empty-refinement-branches noRefinementBypass=empty-refinement-branches + } + + function nestedSingleReadPhis(firstCondition, secondCondition) { + let value; + if (firstCondition) { + if (secondCondition) { + value = SOURCE("nested-single-read-phis"); + } else { + value = "safe"; + } + otherOperation(); + } else { + value = "safe"; + } + SINK(value); // $ refinementFlow=nested-single-read-phis refinementTaint=nested-single-read-phis localRefinementFlow=nested-single-read-phis + } + + function nestedMultipleReadPhis(firstCondition, secondCondition, thirdCondition) { + let value; + if (firstCondition) { + if (secondCondition) { + value = SOURCE("nested-multiple-read-phis"); + } else { + value = "safe"; + } + otherOperation(); + } else { + value = "safe"; + } + if (thirdCondition) { + SINK(value); // $ refinementFlow=nested-multiple-read-phis refinementTaint=nested-multiple-read-phis localRefinementFlow=nested-multiple-read-phis + } else { + use(value); + } + } + + function refinementAfterGuardedJoin(condition) { + const value = SOURCE("refinement-after-guarded-join"); + if (value) { + if (condition) { + if (!BARRIER(value)) return; + } else { + if (!BARRIER(value)) return; + } + if (typeof value === "object") { + SINK(value); // $ localRefinementFlow=refinement-after-guarded-join noRefinementBypass=refinement-after-guarded-join noRefinementFlow=refinement-after-guarded-join noRefinementTaint=refinement-after-guarded-join + } + } + } + + function conditionalReadInsideFalsyRefinement(condition) { + const value = SOURCE("conditional-read-in-refinement"); + if (!value) { + if (condition) { + use(value); + } + SINK(value); // $ barrier=conditional-read-in-refinement noRefinementTaint=conditional-read-in-refinement localRefinementFlow=conditional-read-in-refinement noRefinementFlow=conditional-read-in-refinement noRefinementBypass=conditional-read-in-refinement + } + } + + function independentPhiInputRefinements(condition, otherCondition) { + let value = SOURCE("independent-phi-inputs"); + if (condition) { + } else if (value) { + value = "safe"; + } + if (otherCondition) { + SINK(value); // $ localRefinementFlow=independent-phi-inputs refinementFlow=independent-phi-inputs refinementTaint=independent-phi-inputs + } else { + use(value); + } + } + + function occurrenceSpecificBarrier() { + const value = SOURCE("occurrence-barrier"); + BLOCKED_SINK(value); // $ noRefinementFlow=occurrence-barrier noRefinementTaint=occurrence-barrier localRefinementFlow=occurrence-barrier + SINK(value); // $ localRefinementFlow=occurrence-barrier refinementFlow=occurrence-barrier refinementTaint=occurrence-barrier + } + + function guardedCallResultDoesNotValidateArgument() { + const value = SOURCE("guarded-call-result"); + if (!BARRIER(transform(value))) return; + SINK(value); // $ localRefinementFlow=guarded-call-result refinementFlow=guarded-call-result refinementTaint=guarded-call-result + } + + function occurrenceSpecificBarriersBeforeJoin(condition) { + const value = SOURCE("occurrence-barriers-before-join"); + if (condition) { + BLOCKED_SINK(value); // $ noRefinementFlow=occurrence-barriers-before-join noRefinementTaint=occurrence-barriers-before-join localRefinementFlow=occurrence-barriers-before-join + } else { + BLOCKED_SINK(value); // $ noRefinementFlow=occurrence-barriers-before-join noRefinementTaint=occurrence-barriers-before-join localRefinementFlow=occurrence-barriers-before-join + } + SINK(value); // $ refinementFlow=occurrence-barriers-before-join refinementTaint=occurrence-barriers-before-join localRefinementFlow=occurrence-barriers-before-join + } + + function readOccurrenceSourceDoesNotContaminateVariable() { + const value = "safe"; + READ_SOURCE(value, "read-occurrence-source"); + SINK(value); // $ noRefinementFlow=read-occurrence-source noRefinementTaint=read-occurrence-source + } + + function indirectBarrierGuardWithReadBeforeJoin() { + let value = SOURCE("indirect-barrier-read-join"); + const ok = BARRIER(value); + if (ok) { + use(value); + } else { + value = "safe"; + } + SINK(value); // $ localRefinementFlow=indirect-barrier-read-join noRefinementFlow=indirect-barrier-read-join noRefinementTaint=indirect-barrier-read-join + } + + function indirectBarrierGuardEmptyBranchBeforeJoin() { + let value = SOURCE("indirect-barrier-empty-join"); + const ok = BARRIER(value); + if (ok) { + } else { + value = "safe"; + } + SINK(value); // $ localRefinementFlow=indirect-barrier-empty-join noRefinementFlow=indirect-barrier-empty-join noRefinementTaint=indirect-barrier-empty-join + } + + function indirectBarrierGuardAfterRefinementBeforeJoin() { + let value = SOURCE("indirect-barrier-after-refinement-join"); + const ok = BARRIER(value); + if (typeof value === "object") { + if (ok) { + } else { + value = "safe"; + } + SINK(value); // $ localRefinementFlow=indirect-barrier-after-refinement-join noRefinementBypass=indirect-barrier-after-refinement-join noRefinementFlow=indirect-barrier-after-refinement-join noRefinementTaint=indirect-barrier-after-refinement-join + } + } + + function barrierGuardInsideWithStatement(scopeObject) { + let value = SOURCE("barrier-in-with"); + with (scopeObject) { + if (BARRIER(value)) { + use(value); + } else { + value = "safe"; + } + } + SINK(value); // $ localRefinementFlow=barrier-in-with noRefinementFlow=barrier-in-with noRefinementTaint=barrier-in-with + } + + function mutatePreservedArgument(target, payload) { + target.property = payload; + } + + function selfAssignedArgument(value) { + mutatePreservedArgument(value, SOURCE("self-assigned-argument"), value = value); + SINK(value.property); // $ refinementFlow=self-assigned-argument refinementTaint=self-assigned-argument + } + + function wrappedSelfAssignedArgument(value) { + mutatePreservedArgument(value, (value = (use(), value), SOURCE("wrapped-self-assigned-argument"))); + SINK(value.property); // $ refinementFlow=wrapped-self-assigned-argument refinementTaint=wrapped-self-assigned-argument + } + + function selfAssignedStore(value) { + value.property = (value = value, SOURCE("self-assigned-store")); + SINK(value.property); // $ refinementFlow=self-assigned-store refinementTaint=self-assigned-store + } + + function copiedArgument(value) { + const alias = value; + mutatePreservedArgument(value, SOURCE("copied-argument"), value = alias); + SINK(value.property); // $ refinementFlow=copied-argument refinementTaint=copied-argument + } + + function conditionalArgumentCopy(value, condition) { + mutatePreservedArgument(value, SOURCE("conditional-argument-copy"), value = condition ? value : {}); + SINK(value.property); // $ refinementFlow=conditional-argument-copy refinementTaint=conditional-argument-copy + } + + function identicalBranchArgumentCopy(value, condition) { + mutatePreservedArgument(value, SOURCE("identical-branch-copy"), value = condition ? value : value); + SINK(value.property); // $ refinementFlow=identical-branch-copy refinementTaint=identical-branch-copy + } + + function logicalDefaultArgument(value) { + mutatePreservedArgument(value, SOURCE("logical-default-argument"), value = value || {}); + SINK(value.property); // $ refinementFlow=logical-default-argument refinementTaint=logical-default-argument + } + + function nullishDefaultArgument(value) { + mutatePreservedArgument(value, SOURCE("nullish-default-argument"), value = value ?? {}); + SINK(value.property); // $ refinementFlow=nullish-default-argument refinementTaint=nullish-default-argument + } + + function logicalAndArgument(value, condition) { + mutatePreservedArgument(value, SOURCE("logical-and-argument"), value = condition && value); + SINK(value.property); // $ refinementFlow=logical-and-argument refinementTaint=logical-and-argument + } + + function nestedBranchArgument(value, condition) { + mutatePreservedArgument(value, SOURCE("nested-branch-argument"), value = condition ? (use(), value) : value ?? {}); + SINK(value.property); // $ refinementFlow=nested-branch-argument refinementTaint=nested-branch-argument + } + + function conditionalAliasArgument(value, condition) { + const alias = condition ? value : {}; + mutatePreservedArgument(value, SOURCE("conditional-alias-argument"), value = alias); + SINK(value.property); // $ refinementFlow=conditional-alias-argument refinementTaint=conditional-alias-argument + } + + function conditionalStoreCopy(value, condition) { + value.property = (value = condition ? value : {}, SOURCE("conditional-store-copy")); + SINK(value.property); // $ refinementFlow=conditional-store-copy refinementTaint=conditional-store-copy + } + + function conditionalReplacement(value) { + mutatePreservedArgument(value, SOURCE("conditional-replacement"), value = value ? {} : {}); + SINK(value.property); // $ noRefinementFlow=conditional-replacement noRefinementTaint=conditional-replacement + } + + function discardedValueReplacement(value) { + mutatePreservedArgument(value, SOURCE("discarded-value-replacement"), value = (value, {})); + SINK(value.property); // $ noRefinementFlow=discarded-value-replacement noRefinementTaint=discarded-value-replacement + } + + function identityPreservedArgument(value) { + return value; + } + + function replacementPreservedArgument(unused) { + return {}; + } + + function arrayDestructuringArgumentCopy(value) { + mutatePreservedArgument(value, SOURCE("array-destructuring-copy"), [value] = [value]); + SINK(value.property); // $ refinementFlow=array-destructuring-copy refinementTaint=array-destructuring-copy + } + + function objectDestructuringArgumentCopy(value) { + mutatePreservedArgument(value, SOURCE("object-destructuring-copy"), { slot: value } = { slot: value }); + SINK(value.property); // $ refinementFlow=object-destructuring-copy refinementTaint=object-destructuring-copy + } + + function objectLiteralArgumentCopy(value) { + mutatePreservedArgument(value, SOURCE("object-literal-copy"), value = ({ slot: value }).slot); + SINK(value.property); // $ refinementFlow=object-literal-copy refinementTaint=object-literal-copy + } + + function arrayLiteralArgumentCopy(value) { + mutatePreservedArgument(value, SOURCE("array-literal-copy"), value = [value][0]); + SINK(value.property); // $ refinementFlow=array-literal-copy refinementTaint=array-literal-copy + } + + function identityCallArgumentCopy(value) { + mutatePreservedArgument(value, SOURCE("identity-call-copy"), value = identityPreservedArgument(value)); + SINK(value.property); // $ refinementFlow=identity-call-copy refinementTaint=identity-call-copy + } + + function identityCopyUpdateTiming(value) { + mutatePreservedArgument( + value, SOURCE("identity-copy-timing"), value = identityPreservedArgument(value), + SINK(value.property) // $ noRefinementFlow=identity-copy-timing noRefinementTaint=identity-copy-timing + ); + SINK(value.property); // $ refinementFlow=identity-copy-timing refinementTaint=identity-copy-timing + } + + function identityCallReplacement(value) { + mutatePreservedArgument(value, SOURCE("identity-call-replacement"), value = replacementPreservedArgument(value)); + SINK(value.property); // $ noRefinementFlow=identity-call-replacement noRefinementTaint=identity-call-replacement + } + + function destructuringArgumentReplacement(value) { + mutatePreservedArgument(value, SOURCE("destructuring-argument-replacement"), [value] = [{}]); + SINK(value.property); // $ noRefinementFlow=destructuring-argument-replacement noRefinementTaint=destructuring-argument-replacement + } + + function objectLiteralArgumentReplacement(value) { + mutatePreservedArgument(value, SOURCE("object-literal-replacement"), value = ({ slot: {} }).slot); + SINK(value.property); // $ noRefinementFlow=object-literal-replacement noRefinementTaint=object-literal-replacement + } + + function composedModeledCopy(value) { + mutatePreservedArgument( + value, SOURCE("composed-modeled-copy"), [value] = [identityPreservedArgument(value)] + ); + SINK(value.property); // $ refinementFlow=composed-modeled-copy refinementTaint=composed-modeled-copy + } + + function wrappedReplacementArgument(value) { + function wrap(input) { + return { nested: input }; + } + mutatePreservedArgument(value, SOURCE("wrapped-replacement-argument"), value = wrap(value)); + SINK(value.property); // $ noRefinementFlow=wrapped-replacement-argument noRefinementTaint=wrapped-replacement-argument + } + + function truthyReplacement(value) { + mutatePreservedArgument(value, SOURCE("truthy-replacement"), value = {} || value); + SINK(value.property); // $ SPURIOUS: refinementFlow=truthy-replacement refinementTaint=truthy-replacement + } + + function nonNullishReplacement(value) { + mutatePreservedArgument(value, SOURCE("non-nullish-replacement"), value = {} ?? value); + SINK(value.property); // $ SPURIOUS: refinementFlow=non-nullish-replacement refinementTaint=non-nullish-replacement + } + + // These copy-model false positives also occur with the baseline data-flow library. + function clonedArgument(value) { + mutatePreservedArgument(value, SOURCE("cloned-argument"), value = Object.assign({}, value)); + SINK(value.property); // $ SPURIOUS: refinementFlow=cloned-argument refinementTaint=cloned-argument + } + + function libraryClonedArgument(value) { + const clone = require("clone"); + mutatePreservedArgument(value, SOURCE("library-cloned-argument"), value = clone(value)); + SINK(value.property); // $ SPURIOUS: refinementFlow=library-cloned-argument refinementTaint=library-cloned-argument + } + + function mixedReplacementArgument(value, condition) { + mutatePreservedArgument( + value, SOURCE("mixed-replacement-argument"), + value = condition ? ({} || value) : replacementPreservedArgument(value) + ); + SINK(value.property); // $ SPURIOUS: refinementFlow=mixed-replacement-argument refinementTaint=mixed-replacement-argument + } + + function mixedNullishReplacementArgument(value, condition) { + mutatePreservedArgument( + value, SOURCE("mixed-nullish-replacement-argument"), + value = condition ? ({} ?? value) : replacementPreservedArgument(value) + ); + SINK(value.property); // $ SPURIOUS: refinementFlow=mixed-nullish-replacement-argument refinementTaint=mixed-nullish-replacement-argument + } + + function assignOriginalArgument(value) { + mutatePreservedArgument(value, SOURCE("assign-original-argument"), value = Object.assign(value, {})); + SINK(value.property); // $ refinementFlow=assign-original-argument refinementTaint=assign-original-argument + } + + function constantConditionReplacement(value) { + mutatePreservedArgument(value, SOURCE("constant-condition-replacement"), value = true ? {} : value); + SINK(value.property); // $ noRefinementFlow=constant-condition-replacement noRefinementTaint=constant-condition-replacement + } + + function falsyReplacement(value) { + mutatePreservedArgument(value, SOURCE("falsy-replacement"), value = false && value); + SINK(value.property); // $ noRefinementFlow=falsy-replacement noRefinementTaint=falsy-replacement + } + + function replacedArgument(value) { + mutatePreservedArgument(value, SOURCE("replaced-argument"), value = {}); + SINK(value.property); // $ noRefinementFlow=replaced-argument noRefinementTaint=replaced-argument + } + + function replacedStore(value) { + value.property = (value = {}, SOURCE("replaced-store")); + SINK(value.property); // $ noRefinementFlow=replaced-store noRefinementTaint=replaced-store + } + + function overwrittenCopiedArgument(value) { + let alias = value; + alias = {}; + mutatePreservedArgument(value, SOURCE("overwritten-copied-argument"), value = alias); + SINK(value.property); // $ noRefinementFlow=overwritten-copied-argument noRefinementTaint=overwritten-copied-argument + } + + class SharedSsaThisTests { + guardedThisMethod(condition) { + if (condition) { + if (!BARRIER(this)) return; + } else { + if (!BARRIER(this)) return; + } + SINK(this); // $ localRefinementFlow=this-guard noRefinementFlow=this-guard noRefinementTaint=this-guard + } + + occurrenceBarrierThisMethod() { + this.property = SOURCE("this-occurrence-barrier"); + BLOCKED_SINK(this.property); // $ noRefinementFlow=this-occurrence-barrier noRefinementTaint=this-occurrence-barrier + BLOCKED_SINK(this); // $ noRefinementFlow=this-occurrence-barrier noRefinementTaint=this-occurrence-barrier + SINK(this.property); // $ refinementFlow=this-occurrence-barrier refinementTaint=this-occurrence-barrier + } + } +} diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.ql new file mode 100644 index 000000000000..7ffe1838f2e3 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/refinement.ql @@ -0,0 +1,119 @@ +import javascript +import utils.test.InlineExpectationsTest + +module RefinementConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + source.(DataFlow::InvokeNode).getCalleeName() = "SOURCE" + or + source = any(DataFlow::InvokeNode call | call.getCalleeName() = "READ_SOURCE").getArgument(0) + or + exists(Function method | + method = any(MethodDeclaration decl | decl.getName() = "guardedThisMethod").getBody() and + source = DataFlow::thisNode(method) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::InvokeNode call | + call.getCalleeName() = ["SINK", "BLOCKED_SINK"] and + sink = call.getArgument(0) + ) + } + + predicate isBarrier(DataFlow::Node node) { + node instanceof DataFlow::VarAccessBarrier + or + node = DataFlow::MakeBarrierGuard::getABarrierNode() + or + node = any(DataFlow::InvokeNode call | call.getCalleeName() = "BLOCKED_SINK").getArgument(0) + } +} + +module RefinementFlow = DataFlow::Global; + +module RefinementTaint = TaintTracking::Global; + +class SimpleBarrierGuard extends DataFlow::InvokeNode { + SimpleBarrierGuard() { this.getCalleeName() = "BARRIER" } + + predicate blocksExpr(boolean outcome, Expr expr) { + outcome = true and + expr = this.getArgument(0).asExpr() + } +} + +private predicate localStepWithoutRefinement(DataFlow::Node pred, DataFlow::Node succ) { + DataFlow::localFlowStep(pred, succ) and + not succ.(DataFlow::SsaDefinitionNode).getSsaVariable().getDefinition() instanceof + SsaRefinementNode +} + +module InlineTest implements TestSig { + string getARelevantTag() { + result = + [ + "barrier", "refinementFlow", "noRefinementFlow", "refinementTaint", "noRefinementTaint", + "localRefinementFlow", "noRefinementBypass" + ] + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + element = "" and + location.getFile().getBaseName() = ["refinement.js", "namespace.ts"] and + exists(DataFlow::Node source, DataFlow::InvokeNode sink, Expr sinkArgument, boolean allowLocal | + ( + exists(DataFlow::InvokeNode sourceCall | + sourceCall.getCalleeName() = "SOURCE" and + source = sourceCall and + value = sourceCall.getArgument(0).getStringValue() and + allowLocal = true + or + sourceCall.getCalleeName() = "READ_SOURCE" and + source = sourceCall.getArgument(0) and + value = sourceCall.getArgument(1).getStringValue() and + allowLocal = false + ) + or + exists(MethodDeclaration decl | + source = DataFlow::thisNode(decl.getBody()) and + allowLocal = true and + decl.getName() = "guardedThisMethod" and + value = "this-guard" + ) + ) and + sink.getCalleeName() = ["SINK", "BLOCKED_SINK"] and + source.getContainer() = sink.getContainer() and + sink.getArgument(0) = DataFlow::valueNode(sinkArgument) and + ( + sinkArgument instanceof VarRef or + sinkArgument instanceof ThisExpr or + sinkArgument instanceof PropAccess + ) and + location = sink.getLocation() and + ( + tag = "refinementFlow" and RefinementFlow::flow(source, sink.getArgument(0)) + or + tag = "noRefinementFlow" and not RefinementFlow::flow(source, sink.getArgument(0)) + or + tag = "refinementTaint" and RefinementTaint::flow(source, sink.getArgument(0)) + or + tag = "noRefinementTaint" and not RefinementTaint::flow(source, sink.getArgument(0)) + or + tag = "localRefinementFlow" and + allowLocal = true and + source.getASuccessor*() = sink.getArgument(0) + or + tag = "noRefinementBypass" and + allowLocal = true and + source.getASuccessor*() = sink.getArgument(0) and + not localStepWithoutRefinement*(source, sink.getArgument(0)) + or + tag = "barrier" and + DataFlow::ssaDefinitionNode(sinkArgument.(VarUse).getSsaVariable().getDefinition()) + instanceof DataFlow::VarAccessBarrier + ) + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/test.expected b/javascript/ql/test/library-tests/SharedSsaCompatibility/test.expected new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/test.ql b/javascript/ql/test/library-tests/SharedSsaCompatibility/test.ql new file mode 100644 index 000000000000..10cde765bddd --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/test.ql @@ -0,0 +1,44 @@ +import javascript +import utils.test.InlineExpectationsTest + +module TestConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + exists(SsaPhiNode phi | + phi.getSourceVariable().getName() = "compatibilityPhiSource" and + source = DataFlow::ssaDefinitionNode(phi) + ) + } + + predicate isSink(DataFlow::Node sink) { + exists(DataFlow::InvokeNode call | + call.getCalleeName() = "SINK" and + sink = call.getArgument(0) + ) + } +} + +module TestFlow = DataFlow::Global; + +module InlineTest implements TestSig { + string getARelevantTag() { result = ["source", "flow"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = ["source", "flow"] and + element = "" and + value = "compatibility-phi" and + exists(DataFlow::Node source, DataFlow::InvokeNode sink, VarRef sinkArgument | + TestConfig::isSource(source) and + sink.getCalleeName() = "SINK" and + sink.getArgument(0) = DataFlow::valueNode(sinkArgument) and + sinkArgument.getVariable().getName() = "compatibilityPhiSource" and + location = sink.getLocation() and + ( + tag = "source" + or + tag = "flow" and TestFlow::flow(source, sink.getArgument(0)) + ) + ) + } +} + +import MakeTest diff --git a/javascript/ql/test/library-tests/SharedSsaCompatibility/tst.js b/javascript/ql/test/library-tests/SharedSsaCompatibility/tst.js new file mode 100644 index 000000000000..cc0b1c93f7a1 --- /dev/null +++ b/javascript/ql/test/library-tests/SharedSsaCompatibility/tst.js @@ -0,0 +1,10 @@ +function compatibilityPhi(start, end) { + let compatibilityPhiSource; + for ( + compatibilityPhiSource = start; + compatibilityPhiSource < end; + compatibilityPhiSource++ + ) { + } + SINK(compatibilityPhiSource); // $ source=compatibility-phi flow=compatibility-phi +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 0083e55e642e..5109c89f9847 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -4,6 +4,7 @@ legacyDataFlowDifference | arrays-init.js:2:16:2:23 | source() | arrays-init.js:35:8:35:13 | arr[2] | only flow with OLD data flow library | | arrays-init.js:2:16:2:23 | source() | arrays-init.js:36:8:36:13 | arr[3] | only flow with OLD data flow library | | arrays-init.js:2:16:2:23 | source() | arrays-init.js:37:8:37:13 | arr[4] | only flow with OLD data flow library | +| booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x | only flow with OLD data flow library | | bound-function.js:27:8:27:15 | source() | bound-function.js:30:10:30:10 | y | only flow with OLD data flow library | | call-apply.js:27:14:27:21 | source() | call-apply.js:24:8:24:11 | arg1 | only flow with NEW data flow library | | call-apply.js:27:14:27:21 | source() | call-apply.js:32:6:32:35 | foo1.ap ... e, ""]) | only flow with NEW data flow library | @@ -31,7 +32,6 @@ legacyDataFlowDifference | object-bypass-sanitizer.js:35:29:35:36 | source() | object-bypass-sanitizer.js:23:14:23:20 | obj.foo | only flow with OLD data flow library | | object-bypass-sanitizer.js:35:29:35:36 | source() | object-bypass-sanitizer.js:28:10:28:30 | sanitiz ... bj).foo | only flow with OLD data flow library | | promise.js:12:20:12:27 | source() | promise.js:13:8:13:23 | resolver.promise | only flow with OLD data flow library | -| sanitizer-guards.js:57:11:57:18 | source() | sanitizer-guards.js:64:8:64:8 | x | only flow with NEW data flow library | | spread.js:4:15:4:22 | source() | spread.js:18:8:18:8 | y | only flow with NEW data flow library | | spread.js:4:15:4:22 | source() | spread.js:24:8:24:8 | y | only flow with NEW data flow library | | tst.js:2:13:2:20 | source() | tst.js:17:10:17:10 | a | only flow with OLD data flow library | @@ -85,7 +85,6 @@ flow | arrays.js:2:15:2:22 | source() | arrays.js:21:10:21:19 | foo.at(-1) | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:4:8:4:8 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:13:10:13:10 | x | -| booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:22:10:22:10 | x | | bound-function.js:17:21:17:28 | source() | bound-function.js:5:10:5:16 | y.test2 | | bound-function.js:19:15:19:22 | source() | bound-function.js:6:10:6:16 | y.test3 | @@ -265,7 +264,6 @@ flow | sanitizer-guards.js:13:14:13:21 | source() | sanitizer-guards.js:26:9:26:14 | this.x | | sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:45:8:45:8 | x | | sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:48:10:48:10 | x | -| sanitizer-guards.js:57:11:57:18 | source() | sanitizer-guards.js:64:8:64:8 | x | | sanitizer-guards.js:68:11:68:18 | source() | sanitizer-guards.js:75:8:75:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected index 6335d6dbd49a..4f3002a8aa31 100644 --- a/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/DataFlowTracking.expected @@ -1,5 +1,6 @@ legacyDataFlowDifference | arrays-init.js:2:16:2:23 | source() | arrays-init.js:38:8:38:13 | arr[5] | only flow with NEW data flow library | +| booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x | only flow with OLD data flow library | | bound-function.js:27:8:27:15 | source() | bound-function.js:30:10:30:10 | y | only flow with OLD data flow library | | call-apply.js:27:14:27:21 | source() | call-apply.js:24:8:24:11 | arg1 | only flow with NEW data flow library | | call-apply.js:27:14:27:21 | source() | call-apply.js:32:6:32:35 | foo1.ap ... e, ""]) | only flow with NEW data flow library | @@ -22,7 +23,6 @@ legacyDataFlowDifference | nested-props.js:14:15:14:22 | source() | nested-props.js:15:10:15:16 | obj.x.y | only flow with NEW data flow library | | nested-props.js:27:18:27:25 | source() | nested-props.js:28:10:28:14 | obj.x | only flow with NEW data flow library | | nested-props.js:51:22:51:29 | source() | nested-props.js:52:10:52:16 | obj.x.y | only flow with NEW data flow library | -| sanitizer-guards.js:57:11:57:18 | source() | sanitizer-guards.js:64:8:64:8 | x | only flow with NEW data flow library | | spread.js:4:15:4:22 | source() | spread.js:18:8:18:8 | y | only flow with NEW data flow library | | spread.js:4:15:4:22 | source() | spread.js:24:8:24:8 | y | only flow with NEW data flow library | | tst.js:2:13:2:20 | source() | tst.js:35:14:35:16 | ary | only flow with NEW data flow library | @@ -44,7 +44,6 @@ flow | booleanOps.js:2:11:2:18 | source() | booleanOps.js:7:10:7:10 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:10:10:10:10 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:13:10:13:10 | x | -| booleanOps.js:2:11:2:18 | source() | booleanOps.js:19:10:19:10 | x | | booleanOps.js:2:11:2:18 | source() | booleanOps.js:22:10:22:10 | x | | bound-function.js:17:21:17:28 | source() | bound-function.js:5:10:5:16 | y.test2 | | bound-function.js:19:15:19:22 | source() | bound-function.js:6:10:6:16 | y.test3 | @@ -174,7 +173,6 @@ flow | sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:45:8:45:8 | x | | sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:48:10:48:10 | x | | sanitizer-guards.js:43:11:43:18 | source() | sanitizer-guards.js:52:10:52:10 | x | -| sanitizer-guards.js:57:11:57:18 | source() | sanitizer-guards.js:64:8:64:8 | x | | sanitizer-guards.js:68:11:68:18 | source() | sanitizer-guards.js:75:8:75:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:81:8:81:8 | x | | sanitizer-guards.js:79:11:79:18 | source() | sanitizer-guards.js:84:10:84:10 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/booleanOps.js b/javascript/ql/test/library-tests/TaintTracking/booleanOps.js index 6cb0d6cea338..55bec4306aec 100644 --- a/javascript/ql/test/library-tests/TaintTracking/booleanOps.js +++ b/javascript/ql/test/library-tests/TaintTracking/booleanOps.js @@ -16,7 +16,7 @@ function test() { sink(x); // OK if (isSafe(x, y) || isSafe(x, z)) - sink(x); // OK [INCONSISTENCY] + sink(x); // OK if (isSafe(x) || 1 === 1) sink(x); // NOT OK diff --git a/javascript/ql/test/library-tests/TaintTracking/sanitizer-guards.js b/javascript/ql/test/library-tests/TaintTracking/sanitizer-guards.js index 14f4139ca083..bccd4f1f0f17 100644 --- a/javascript/ql/test/library-tests/TaintTracking/sanitizer-guards.js +++ b/javascript/ql/test/library-tests/TaintTracking/sanitizer-guards.js @@ -61,7 +61,7 @@ function phi() { } else { x = null; } - sink(x); // OK [INCONSISTENCY] - dataflow2 cannot block the phi edge + sink(x); // OK } function phi2() { diff --git a/javascript/ql/test/library-tests/frameworks/AngularJS/scopes/ScopeAccess.expected b/javascript/ql/test/library-tests/frameworks/AngularJS/scopes/ScopeAccess.expected index 1560c2076bc1..8a10bcd8841a 100644 --- a/javascript/ql/test/library-tests/frameworks/AngularJS/scopes/ScopeAccess.expected +++ b/javascript/ql/test/library-tests/frameworks/AngularJS/scopes/ScopeAccess.expected @@ -59,6 +59,8 @@ | scope for
... | dev-guide-5.js:4:47:4:52 | $scope | | scope for
... | dev-guide-5.js:5:3:5:8 | $scope | | scope for
... | dev-guide-5.js:5:3:5:8 | $scope | +| scope for
... | dev-guide-5.js:5:3:5:64 | [ssa-synth-read] $scope | +| scope for
... | dev-guide-5.js:5:3:5:64 | [ssa-synth-read] $scope | | scope for
... | dev-guide-5.js:6:3:6:8 | $scope | | scope for
... | dev-guide-5.js:6:3:6:8 | $scope | | scope for
... | dev-guide-6.js:4:47:4:52 | $scope | @@ -67,6 +69,8 @@ | scope for
... | dev-guide-6.js:4:47:4:52 | $scope | | scope for
... | dev-guide-6.js:5:3:5:8 | $scope | | scope for
... | dev-guide-6.js:5:3:5:8 | $scope | +| scope for
... | dev-guide-6.js:5:3:5:64 | [ssa-synth-read] $scope | +| scope for
... | dev-guide-6.js:5:3:5:64 | [ssa-synth-read] $scope | | scope for
... | dev-guide-6.js:6:3:6:8 | $scope | | scope for
... | dev-guide-6.js:6:3:6:8 | $scope | | scope for ... | scope-access.js:59:52:59:57 | $scope | @@ -116,18 +120,22 @@ | scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope | | scope in dev-guide-5.html | dev-guide-5.js:4:47:4:52 | $scope | | scope in dev-guide-5.html | dev-guide-5.js:5:3:5:8 | $scope | +| scope in dev-guide-5.html | dev-guide-5.js:5:3:5:64 | [ssa-synth-read] $scope | | scope in dev-guide-5.html | dev-guide-5.js:6:3:6:8 | $scope | | scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope | | scope in dev-guide-5.html | dev-guide-6.js:4:47:4:52 | $scope | | scope in dev-guide-5.html | dev-guide-6.js:5:3:5:8 | $scope | +| scope in dev-guide-5.html | dev-guide-6.js:5:3:5:64 | [ssa-synth-read] $scope | | scope in dev-guide-5.html | dev-guide-6.js:6:3:6:8 | $scope | | scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope | | scope in dev-guide-6.html | dev-guide-5.js:4:47:4:52 | $scope | | scope in dev-guide-6.html | dev-guide-5.js:5:3:5:8 | $scope | +| scope in dev-guide-6.html | dev-guide-5.js:5:3:5:64 | [ssa-synth-read] $scope | | scope in dev-guide-6.html | dev-guide-5.js:6:3:6:8 | $scope | | scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope | | scope in dev-guide-6.html | dev-guide-6.js:4:47:4:52 | $scope | | scope in dev-guide-6.html | dev-guide-6.js:5:3:5:8 | $scope | +| scope in dev-guide-6.html | dev-guide-6.js:5:3:5:64 | [ssa-synth-read] $scope | | scope in dev-guide-6.html | dev-guide-6.js:6:3:6:8 | $scope | | scope in scope-access.html | scope-access.js:54:34:54:39 | $scope | | scope in scope-access.html | scope-access.js:54:34:54:39 | $scope | diff --git a/javascript/ql/test/library-tests/frameworks/Electron/tests.expected b/javascript/ql/test/library-tests/frameworks/Electron/tests.expected index a51c8e632b18..52f9836fae72 100644 --- a/javascript/ql/test/library-tests/frameworks/Electron/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Electron/tests.expected @@ -14,6 +14,8 @@ browserObject | electron.js:62:7:62:9 | win | | electron.js:62:13:62:59 | new Bro ... 1500 }) | | electron.js:63:3:63:5 | win | +| electron.js:63:3:63:13 | [ssa-synth-read] win | +| electron.js:63:3:63:34 | [ssa-synth-read] win | | electron.js:65:18:65:20 | win | | electronTs.ts:3:12:3:13 | bw | | electronTs.ts:3:12:3:13 | bw | diff --git a/javascript/ql/test/library-tests/frameworks/Express/tests.expected b/javascript/ql/test/library-tests/frameworks/Express/tests.expected index 9007a1ed984e..2eaa18989a19 100644 --- a/javascript/ql/test/library-tests/frameworks/Express/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/Express/tests.expected @@ -84,6 +84,8 @@ test_isRequest | src/express3.js:4:32:4:34 | req | | src/express3.js:4:32:4:34 | req | | src/express3.js:5:14:5:16 | req | +| src/express3.js:5:14:5:22 | [ssa-synth-read] req | +| src/express3.js:5:14:5:32 | [ssa-synth-read] req | | src/express3.js:5:35:5:37 | req | | src/express3.js:10:22:10:24 | req | | src/express3.js:15:20:15:22 | req | @@ -91,30 +93,46 @@ test_isRequest | src/express4.js:4:32:4:34 | req | | src/express4.js:4:32:4:34 | req | | src/express4.js:5:27:5:29 | req | +| src/express4.js:5:27:5:35 | [ssa-synth-read] req | | src/express4.js:6:18:6:20 | req | +| src/express4.js:6:18:6:26 | [ssa-synth-read] req | | src/express4.js:7:18:7:20 | req | | src/express.js:4:32:4:34 | req | | src/express.js:4:32:4:34 | req | | src/express.js:5:16:5:18 | req | +| src/express.js:5:16:5:24 | [ssa-synth-read] req | +| src/express.js:5:16:5:34 | [ssa-synth-read] req | | src/express.js:6:26:6:28 | req | | src/express.js:16:28:16:30 | req | | src/express.js:22:39:22:41 | req | | src/express.js:22:39:22:41 | req | | src/express.js:23:3:23:5 | req | +| src/express.js:23:3:23:10 | [ssa-synth-read] req | | src/express.js:24:3:24:5 | req | +| src/express.js:24:3:24:12 | [ssa-synth-read] req | | src/express.js:25:3:25:5 | req | +| src/express.js:25:3:25:11 | [ssa-synth-read] req | | src/express.js:26:3:26:5 | req | +| src/express.js:26:3:26:9 | [ssa-synth-read] req | | src/express.js:27:3:27:5 | req | +| src/express.js:27:3:27:17 | [ssa-synth-read] req | | src/express.js:28:3:28:5 | req | +| src/express.js:28:3:28:9 | [ssa-synth-read] req | +| src/express.js:28:3:28:16 | [ssa-synth-read] req | | src/express.js:29:3:29:5 | req | +| src/express.js:29:3:29:12 | [ssa-synth-read] req | +| src/express.js:29:3:29:19 | [ssa-synth-read] req | | src/express.js:30:3:30:5 | req | | src/express.js:37:19:37:21 | req | | src/express.js:42:11:42:13 | req | | src/express.js:46:31:46:33 | req | | src/express.js:46:31:46:33 | req | | src/express.js:47:3:47:5 | req | +| src/express.js:47:3:47:13 | [ssa-synth-read] req | | src/express.js:48:3:48:5 | req | +| src/express.js:48:3:48:10 | [ssa-synth-read] req | | src/express.js:49:3:49:5 | req | +| src/express.js:49:3:49:14 | [ssa-synth-read] req | | src/express.js:50:3:50:5 | req | | src/express.js:53:32:53:34 | req | | src/express.js:53:32:53:34 | req | @@ -140,12 +158,16 @@ test_isRequest | src/middleware-flow.js:5:20:5:22 | req | | src/middleware-flow.js:5:20:5:22 | req | | src/middleware-flow.js:6:5:6:7 | req | +| src/middleware-flow.js:6:5:6:21 | [ssa-synth-read] req | | src/middleware-flow.js:7:5:7:7 | req | +| src/middleware-flow.js:7:5:7:12 | [ssa-synth-read] req | | src/middleware-flow.js:8:5:8:7 | req | | src/middleware-flow.js:17:25:17:27 | req | | src/middleware-flow.js:17:25:17:27 | req | | src/middleware-flow.js:18:9:18:11 | req | +| src/middleware-flow.js:18:9:18:14 | [ssa-synth-read] req | | src/middleware-flow.js:19:9:19:11 | req | +| src/middleware-flow.js:19:9:19:16 | [ssa-synth-read] req | | src/middleware-flow.js:20:9:20:11 | req | | src/middleware-flow.js:23:18:23:20 | req | | src/middleware-flow.js:23:18:23:20 | req | @@ -156,11 +178,14 @@ test_isRequest | src/middleware-flow.js:39:24:39:26 | req | | src/middleware-flow.js:39:24:39:26 | req | | src/middleware-flow.js:40:5:40:7 | req | +| src/middleware-flow.js:40:5:40:10 | [ssa-synth-read] req | | src/middleware-flow.js:41:5:41:7 | req | +| src/middleware-flow.js:41:5:41:12 | [ssa-synth-read] req | | src/middleware-flow.js:42:5:42:7 | req | | src/params.js:4:19:4:21 | req | | src/params.js:4:19:4:21 | req | | src/params.js:5:17:5:19 | req | +| src/params.js:5:17:5:25 | [ssa-synth-read] req | | src/params.js:6:17:6:19 | req | | src/params.js:14:33:14:35 | req | | src/passport.js:27:13:27:15 | req | @@ -288,6 +313,8 @@ test_isResponse | src/express3.js:4:37:4:39 | res | | src/express3.js:4:37:4:39 | res | | src/express3.js:5:3:5:5 | res | +| src/express3.js:5:3:5:12 | [ssa-synth-read] res | +| src/express3.js:5:3:5:51 | [ssa-synth-read] res | | src/express3.js:5:3:5:51 | res.hea ... "val")) | | src/express3.js:6:3:6:5 | res | | src/express3.js:6:3:6:17 | res.send("val") | @@ -301,9 +328,15 @@ test_isResponse | src/express.js:4:37:4:39 | res | | src/express.js:4:37:4:39 | res | | src/express.js:5:3:5:5 | res | +| src/express.js:5:3:5:14 | [ssa-synth-read] res | +| src/express.js:5:3:5:35 | [ssa-synth-read] res | | src/express.js:6:3:6:5 | res | +| src/express.js:6:3:6:12 | [ssa-synth-read] res | +| src/express.js:6:3:6:45 | [ssa-synth-read] res | | src/express.js:6:3:6:45 | res.hea ... rget")) | | src/express.js:7:3:7:5 | res | +| src/express.js:7:3:7:12 | [ssa-synth-read] res | +| src/express.js:7:3:7:42 | [ssa-synth-read] res | | src/express.js:7:3:7:42 | res.hea ... plain") | | src/express.js:8:7:8:9 | res | | src/express.js:11:14:11:16 | arg | @@ -324,34 +357,52 @@ test_isResponse | src/express.js:53:37:53:39 | res | | src/express.js:53:37:53:39 | res | | src/express.js:54:3:54:5 | res | +| src/express.js:54:3:54:12 | [ssa-synth-read] res | +| src/express.js:54:3:54:41 | [ssa-synth-read] res | | src/express.js:54:3:54:41 | res.hea ... /html") | | src/express.js:55:3:55:5 | res | +| src/express.js:55:3:55:10 | [ssa-synth-read] res | +| src/express.js:55:3:55:26 | [ssa-synth-read] res | | src/express.js:55:3:55:26 | res.sen ... ms.foo) | | src/express.js:56:7:56:9 | res | | src/express.js:59:37:59:39 | res | | src/express.js:59:37:59:39 | res | | src/express.js:60:3:60:5 | res | +| src/express.js:60:3:60:12 | [ssa-synth-read] res | +| src/express.js:60:3:60:47 | [ssa-synth-read] res | | src/express.js:60:3:60:47 | res.hea ... n/xml") | | src/express.js:61:3:61:5 | res | +| src/express.js:61:3:61:10 | [ssa-synth-read] res | +| src/express.js:61:3:61:26 | [ssa-synth-read] res | | src/express.js:61:3:61:26 | res.sen ... ms.foo) | | src/express.js:62:7:62:9 | res | | src/express.js:65:41:65:43 | res | | src/express.js:65:41:65:43 | res | | src/express.js:66:3:66:5 | res | +| src/express.js:66:3:66:12 | [ssa-synth-read] res | +| src/express.js:66:3:66:42 | [ssa-synth-read] res | | src/express.js:66:3:66:42 | res.hea ... plain") | | src/express.js:67:3:67:5 | res | +| src/express.js:67:3:67:10 | [ssa-synth-read] res | +| src/express.js:67:3:67:26 | [ssa-synth-read] res | | src/express.js:67:3:67:26 | res.sen ... ms.foo) | | src/express.js:68:7:68:9 | res | | src/express.js:71:37:71:39 | res | | src/express.js:71:37:71:39 | res | | src/express.js:72:3:72:5 | res | +| src/express.js:72:3:72:12 | [ssa-synth-read] res | +| src/express.js:72:3:72:41 | [ssa-synth-read] res | | src/express.js:72:3:72:41 | res.hea ... /html") | | src/express.js:73:3:73:5 | res | +| src/express.js:73:3:73:10 | [ssa-synth-read] res | +| src/express.js:73:3:73:20 | [ssa-synth-read] res | | src/express.js:73:3:73:20 | res.send(req.path) | | src/express.js:74:7:74:9 | res | | src/inheritedFromNode.js:4:29:4:31 | res | | src/inheritedFromNode.js:4:29:4:31 | res | | src/inheritedFromNode.js:5:2:5:4 | res | +| src/inheritedFromNode.js:5:2:5:8 | [ssa-synth-read] res | +| src/inheritedFromNode.js:5:2:5:10 | [ssa-synth-read] res | | src/inheritedFromNode.js:6:2:6:4 | res | | src/json.js:4:37:4:39 | res | | src/json.js:4:37:4:39 | res | @@ -389,36 +440,68 @@ test_isResponse | src/responseExprs.js:16:44:16:46 | res | | src/responseExprs.js:16:44:16:46 | res | | src/responseExprs.js:19:5:19:7 | res | +| src/responseExprs.js:19:5:19:14 | [ssa-synth-read] res | +| src/responseExprs.js:19:5:19:16 | [ssa-synth-read] res | | src/responseExprs.js:19:5:19:16 | res.append() | | src/responseExprs.js:20:5:20:7 | res | +| src/responseExprs.js:20:5:20:18 | [ssa-synth-read] res | +| src/responseExprs.js:20:5:20:20 | [ssa-synth-read] res | | src/responseExprs.js:20:5:20:20 | res.attachment() | | src/responseExprs.js:21:5:21:7 | res | +| src/responseExprs.js:21:5:21:19 | [ssa-synth-read] res | +| src/responseExprs.js:21:5:21:21 | [ssa-synth-read] res | | src/responseExprs.js:21:5:21:21 | res.clearCookie() | | src/responseExprs.js:22:5:22:7 | res | +| src/responseExprs.js:22:5:22:19 | [ssa-synth-read] res | +| src/responseExprs.js:22:5:22:21 | [ssa-synth-read] res | | src/responseExprs.js:22:5:22:21 | res.contentType() | | src/responseExprs.js:23:5:23:7 | res | +| src/responseExprs.js:23:5:23:14 | [ssa-synth-read] res | +| src/responseExprs.js:23:5:23:16 | [ssa-synth-read] res | | src/responseExprs.js:23:5:23:16 | res.cookie() | | src/responseExprs.js:24:5:24:7 | res | +| src/responseExprs.js:24:5:24:14 | [ssa-synth-read] res | +| src/responseExprs.js:24:5:24:16 | [ssa-synth-read] res | | src/responseExprs.js:24:5:24:16 | res.format() | | src/responseExprs.js:25:5:25:7 | res | +| src/responseExprs.js:25:5:25:14 | [ssa-synth-read] res | +| src/responseExprs.js:25:5:25:16 | [ssa-synth-read] res | | src/responseExprs.js:25:5:25:16 | res.header() | | src/responseExprs.js:26:5:26:7 | res | +| src/responseExprs.js:26:5:26:13 | [ssa-synth-read] res | +| src/responseExprs.js:26:5:26:15 | [ssa-synth-read] res | | src/responseExprs.js:26:5:26:15 | res.links() | | src/responseExprs.js:27:5:27:7 | res | +| src/responseExprs.js:27:5:27:16 | [ssa-synth-read] res | +| src/responseExprs.js:27:5:27:18 | [ssa-synth-read] res | | src/responseExprs.js:27:5:27:18 | res.location() | | src/responseExprs.js:28:5:28:7 | res | +| src/responseExprs.js:28:5:28:12 | [ssa-synth-read] res | +| src/responseExprs.js:28:5:28:14 | [ssa-synth-read] res | | src/responseExprs.js:28:5:28:14 | res.send() | | src/responseExprs.js:29:5:29:7 | res | +| src/responseExprs.js:29:5:29:18 | [ssa-synth-read] res | +| src/responseExprs.js:29:5:29:20 | [ssa-synth-read] res | | src/responseExprs.js:29:5:29:20 | res.sendStatus() | | src/responseExprs.js:30:5:30:7 | res | +| src/responseExprs.js:30:5:30:11 | [ssa-synth-read] res | +| src/responseExprs.js:30:5:30:13 | [ssa-synth-read] res | | src/responseExprs.js:30:5:30:13 | res.set() | | src/responseExprs.js:31:5:31:7 | res | +| src/responseExprs.js:31:5:31:14 | [ssa-synth-read] res | +| src/responseExprs.js:31:5:31:16 | [ssa-synth-read] res | | src/responseExprs.js:31:5:31:16 | res.status() | | src/responseExprs.js:32:5:32:7 | res | +| src/responseExprs.js:32:5:32:12 | [ssa-synth-read] res | +| src/responseExprs.js:32:5:32:14 | [ssa-synth-read] res | | src/responseExprs.js:32:5:32:14 | res.type() | | src/responseExprs.js:33:5:33:7 | res | +| src/responseExprs.js:33:5:33:12 | [ssa-synth-read] res | +| src/responseExprs.js:33:5:33:14 | [ssa-synth-read] res | | src/responseExprs.js:33:5:33:14 | res.vary() | | src/responseExprs.js:35:5:35:7 | res | +| src/responseExprs.js:35:5:35:14 | [ssa-synth-read] res | +| src/responseExprs.js:35:5:35:16 | [ssa-synth-read] res | | src/responseExprs.js:35:5:35:16 | res.status() | | src/responseExprs.js:35:5:35:23 | res.status().send() | | src/responseExprs.js:37:5:37:19 | f(res.append()) | @@ -544,6 +627,8 @@ test_RequestExpr | src/express3.js:4:32:4:34 | req | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:4:32:4:34 | req | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:5:14:5:16 | req | src/express3.js:4:23:7:1 | functio ... al");\\n} | +| src/express3.js:5:14:5:22 | [ssa-synth-read] req | src/express3.js:4:23:7:1 | functio ... al");\\n} | +| src/express3.js:5:14:5:32 | [ssa-synth-read] req | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:5:35:5:37 | req | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:10:22:10:24 | req | src/express3.js:10:12:10:32 | functio ... res){} | | src/express3.js:15:20:15:22 | req | src/express3.js:15:10:15:30 | functio ... res){} | @@ -551,30 +636,46 @@ test_RequestExpr | src/express4.js:4:32:4:34 | req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | | src/express4.js:4:32:4:34 | req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | | src/express4.js:5:27:5:29 | req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | +| src/express4.js:5:27:5:35 | [ssa-synth-read] req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | | src/express4.js:6:18:6:20 | req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | +| src/express4.js:6:18:6:26 | [ssa-synth-read] req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | | src/express4.js:7:18:7:20 | req | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | | src/express.js:4:32:4:34 | req | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:4:32:4:34 | req | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:5:16:5:18 | req | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:5:16:5:24 | [ssa-synth-read] req | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:5:16:5:34 | [ssa-synth-read] req | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:6:26:6:28 | req | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:16:28:16:30 | req | src/express.js:16:19:18:3 | functio ... ");\\n } | | src/express.js:22:39:22:41 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:22:39:22:41 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:23:3:23:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:23:3:23:10 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:24:3:24:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:24:3:24:12 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:25:3:25:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:25:3:25:11 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:26:3:26:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:26:3:26:9 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:27:3:27:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:27:3:27:17 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:28:3:28:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:28:3:28:9 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:28:3:28:16 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:29:3:29:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:29:3:29:12 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | +| src/express.js:29:3:29:19 | [ssa-synth-read] req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:30:3:30:5 | req | src/express.js:22:30:32:1 | functio ... ar');\\n} | | src/express.js:37:19:37:21 | req | src/express.js:37:10:37:31 | functio ... es) { } | | src/express.js:42:11:42:13 | req | src/express.js:42:10:42:26 | (req, res) => f() | | src/express.js:46:31:46:33 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:46:31:46:33 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:47:3:47:5 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | +| src/express.js:47:3:47:13 | [ssa-synth-read] req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:48:3:48:5 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | +| src/express.js:48:3:48:10 | [ssa-synth-read] req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:49:3:49:5 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | +| src/express.js:49:3:49:14 | [ssa-synth-read] req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:50:3:50:5 | req | src/express.js:46:22:51:1 | functio ... ame];\\n} | | src/express.js:53:32:53:34 | req | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:53:32:53:34 | req | src/express.js:53:23:57:1 | functio ... res);\\n} | @@ -600,12 +701,16 @@ test_RequestExpr | src/middleware-flow.js:5:20:5:22 | req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | | src/middleware-flow.js:5:20:5:22 | req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | | src/middleware-flow.js:6:5:6:7 | req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | +| src/middleware-flow.js:6:5:6:21 | [ssa-synth-read] req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | | src/middleware-flow.js:7:5:7:7 | req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | +| src/middleware-flow.js:7:5:7:12 | [ssa-synth-read] req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | | src/middleware-flow.js:8:5:8:7 | req | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | | src/middleware-flow.js:17:25:17:27 | req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | | src/middleware-flow.js:17:25:17:27 | req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | | src/middleware-flow.js:18:9:18:11 | req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | +| src/middleware-flow.js:18:9:18:14 | [ssa-synth-read] req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | | src/middleware-flow.js:19:9:19:11 | req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | +| src/middleware-flow.js:19:9:19:16 | [ssa-synth-read] req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | | src/middleware-flow.js:20:9:20:11 | req | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | | src/middleware-flow.js:23:18:23:20 | req | src/middleware-flow.js:23:17:23:41 | (req, r ... q.db; } | | src/middleware-flow.js:23:18:23:20 | req | src/middleware-flow.js:23:17:23:41 | (req, r ... q.db; } | @@ -616,11 +721,14 @@ test_RequestExpr | src/middleware-flow.js:39:24:39:26 | req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | | src/middleware-flow.js:39:24:39:26 | req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | | src/middleware-flow.js:40:5:40:7 | req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | +| src/middleware-flow.js:40:5:40:10 | [ssa-synth-read] req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | | src/middleware-flow.js:41:5:41:7 | req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | +| src/middleware-flow.js:41:5:41:12 | [ssa-synth-read] req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | | src/middleware-flow.js:42:5:42:7 | req | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | | src/params.js:4:19:4:21 | req | src/params.js:4:18:12:1 | (req, r ... }\\n} | | src/params.js:4:19:4:21 | req | src/params.js:4:18:12:1 | (req, r ... }\\n} | | src/params.js:5:17:5:19 | req | src/params.js:4:18:12:1 | (req, r ... }\\n} | +| src/params.js:5:17:5:25 | [ssa-synth-read] req | src/params.js:4:18:12:1 | (req, r ... }\\n} | | src/params.js:6:17:6:19 | req | src/params.js:4:18:12:1 | (req, r ... }\\n} | | src/params.js:14:33:14:35 | req | src/params.js:14:24:16:1 | functio ... lo");\\n} | | src/passport.js:27:13:27:15 | req | src/passport.js:27:4:29:1 | functio ... ccss`\\n} | @@ -764,6 +872,8 @@ test_ResponseExpr | src/express3.js:4:37:4:39 | res | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:4:37:4:39 | res | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:5:3:5:5 | res | src/express3.js:4:23:7:1 | functio ... al");\\n} | +| src/express3.js:5:3:5:12 | [ssa-synth-read] res | src/express3.js:4:23:7:1 | functio ... al");\\n} | +| src/express3.js:5:3:5:51 | [ssa-synth-read] res | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:5:3:5:51 | res.hea ... "val")) | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:6:3:6:5 | res | src/express3.js:4:23:7:1 | functio ... al");\\n} | | src/express3.js:6:3:6:17 | res.send("val") | src/express3.js:4:23:7:1 | functio ... al");\\n} | @@ -777,9 +887,15 @@ test_ResponseExpr | src/express.js:4:37:4:39 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:4:37:4:39 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:5:3:5:5 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:5:3:5:14 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:5:3:5:35 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:6:3:6:5 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:6:3:6:12 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:6:3:6:45 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:6:3:6:45 | res.hea ... rget")) | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:7:3:7:5 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:7:3:7:12 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | +| src/express.js:7:3:7:42 | [ssa-synth-read] res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:7:3:7:42 | res.hea ... plain") | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:8:7:8:9 | res | src/express.js:4:23:9:1 | functio ... res);\\n} | | src/express.js:11:14:11:16 | arg | src/express.js:4:23:9:1 | functio ... res);\\n} | @@ -816,34 +932,52 @@ test_ResponseExpr | src/express.js:53:37:53:39 | res | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:53:37:53:39 | res | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:54:3:54:5 | res | src/express.js:53:23:57:1 | functio ... res);\\n} | +| src/express.js:54:3:54:12 | [ssa-synth-read] res | src/express.js:53:23:57:1 | functio ... res);\\n} | +| src/express.js:54:3:54:41 | [ssa-synth-read] res | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:54:3:54:41 | res.hea ... /html") | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:55:3:55:5 | res | src/express.js:53:23:57:1 | functio ... res);\\n} | +| src/express.js:55:3:55:10 | [ssa-synth-read] res | src/express.js:53:23:57:1 | functio ... res);\\n} | +| src/express.js:55:3:55:26 | [ssa-synth-read] res | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:55:3:55:26 | res.sen ... ms.foo) | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:56:7:56:9 | res | src/express.js:53:23:57:1 | functio ... res);\\n} | | src/express.js:59:37:59:39 | res | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:59:37:59:39 | res | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:60:3:60:5 | res | src/express.js:59:23:63:1 | functio ... res);\\n} | +| src/express.js:60:3:60:12 | [ssa-synth-read] res | src/express.js:59:23:63:1 | functio ... res);\\n} | +| src/express.js:60:3:60:47 | [ssa-synth-read] res | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:60:3:60:47 | res.hea ... n/xml") | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:61:3:61:5 | res | src/express.js:59:23:63:1 | functio ... res);\\n} | +| src/express.js:61:3:61:10 | [ssa-synth-read] res | src/express.js:59:23:63:1 | functio ... res);\\n} | +| src/express.js:61:3:61:26 | [ssa-synth-read] res | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:61:3:61:26 | res.sen ... ms.foo) | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:62:7:62:9 | res | src/express.js:59:23:63:1 | functio ... res);\\n} | | src/express.js:65:41:65:43 | res | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:65:41:65:43 | res | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:66:3:66:5 | res | src/express.js:65:27:69:1 | functio ... res);\\n} | +| src/express.js:66:3:66:12 | [ssa-synth-read] res | src/express.js:65:27:69:1 | functio ... res);\\n} | +| src/express.js:66:3:66:42 | [ssa-synth-read] res | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:66:3:66:42 | res.hea ... plain") | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:67:3:67:5 | res | src/express.js:65:27:69:1 | functio ... res);\\n} | +| src/express.js:67:3:67:10 | [ssa-synth-read] res | src/express.js:65:27:69:1 | functio ... res);\\n} | +| src/express.js:67:3:67:26 | [ssa-synth-read] res | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:67:3:67:26 | res.sen ... ms.foo) | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:68:7:68:9 | res | src/express.js:65:27:69:1 | functio ... res);\\n} | | src/express.js:71:37:71:39 | res | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:71:37:71:39 | res | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:72:3:72:5 | res | src/express.js:71:23:75:1 | functio ... res);\\n} | +| src/express.js:72:3:72:12 | [ssa-synth-read] res | src/express.js:71:23:75:1 | functio ... res);\\n} | +| src/express.js:72:3:72:41 | [ssa-synth-read] res | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:72:3:72:41 | res.hea ... /html") | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:73:3:73:5 | res | src/express.js:71:23:75:1 | functio ... res);\\n} | +| src/express.js:73:3:73:10 | [ssa-synth-read] res | src/express.js:71:23:75:1 | functio ... res);\\n} | +| src/express.js:73:3:73:20 | [ssa-synth-read] res | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:73:3:73:20 | res.send(req.path) | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/express.js:74:7:74:9 | res | src/express.js:71:23:75:1 | functio ... res);\\n} | | src/inheritedFromNode.js:4:29:4:31 | res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | | src/inheritedFromNode.js:4:29:4:31 | res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | | src/inheritedFromNode.js:5:2:5:4 | res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | +| src/inheritedFromNode.js:5:2:5:8 | [ssa-synth-read] res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | +| src/inheritedFromNode.js:5:2:5:10 | [ssa-synth-read] res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | | src/inheritedFromNode.js:6:2:6:4 | res | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | | src/json.js:4:37:4:39 | res | src/json.js:4:23:6:1 | functio ... ata);\\n} | | src/json.js:4:37:4:39 | res | src/json.js:4:23:6:1 | functio ... ata);\\n} | @@ -881,36 +1015,68 @@ test_ResponseExpr | src/responseExprs.js:16:44:16:46 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:16:44:16:46 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:19:5:19:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:19:5:19:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:19:5:19:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:19:5:19:16 | res.append() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:20:5:20:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:20:5:20:18 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:20:5:20:20 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:20:5:20:20 | res.attachment() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:21:5:21:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:21:5:21:19 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:21:5:21:21 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:21:5:21:21 | res.clearCookie() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:22:5:22:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:22:5:22:19 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:22:5:22:21 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:22:5:22:21 | res.contentType() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:23:5:23:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:23:5:23:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:23:5:23:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:23:5:23:16 | res.cookie() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:24:5:24:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:24:5:24:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:24:5:24:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:24:5:24:16 | res.format() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:25:5:25:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:25:5:25:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:25:5:25:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:25:5:25:16 | res.header() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:26:5:26:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:26:5:26:13 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:26:5:26:15 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:26:5:26:15 | res.links() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:27:5:27:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:27:5:27:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:27:5:27:18 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:27:5:27:18 | res.location() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:28:5:28:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:28:5:28:12 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:28:5:28:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:28:5:28:14 | res.send() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:29:5:29:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:29:5:29:18 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:29:5:29:20 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:29:5:29:20 | res.sendStatus() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:30:5:30:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:30:5:30:11 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:30:5:30:13 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:30:5:30:13 | res.set() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:31:5:31:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:31:5:31:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:31:5:31:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:31:5:31:16 | res.status() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:32:5:32:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:32:5:32:12 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:32:5:32:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:32:5:32:14 | res.type() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:33:5:33:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:33:5:33:12 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:33:5:33:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:33:5:33:14 | res.vary() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:35:5:35:7 | res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:35:5:35:14 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | +| src/responseExprs.js:35:5:35:16 | [ssa-synth-read] res | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:35:5:35:16 | res.status() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:35:5:35:23 | res.status().send() | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | | src/responseExprs.js:37:5:37:19 | f(res.append()) | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | @@ -1710,6 +1876,8 @@ test_RouteHandler_getARequestExpr | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:4:32:4:34 | req | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:4:32:4:34 | req | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:14:5:16 | req | +| src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:14:5:22 | [ssa-synth-read] req | +| src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:14:5:32 | [ssa-synth-read] req | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:35:5:37 | req | | src/express3.js:10:12:10:32 | functio ... res){} | src/express3.js:10:22:10:24 | req | | src/express3.js:15:10:15:30 | functio ... res){} | src/express3.js:15:20:15:22 | req | @@ -1717,30 +1885,46 @@ test_RouteHandler_getARequestExpr | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:4:32:4:34 | req | | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:4:32:4:34 | req | | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:5:27:5:29 | req | +| src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:5:27:5:35 | [ssa-synth-read] req | | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:6:18:6:20 | req | +| src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:6:18:6:26 | [ssa-synth-read] req | | src/express4.js:4:23:9:1 | functio ... ic1);\\n} | src/express4.js:7:18:7:20 | req | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:4:32:4:34 | req | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:4:32:4:34 | req | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:16:5:18 | req | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:16:5:24 | [ssa-synth-read] req | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:16:5:34 | [ssa-synth-read] req | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:6:26:6:28 | req | | src/express.js:16:19:18:3 | functio ... ");\\n } | src/express.js:16:28:16:30 | req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:22:39:22:41 | req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:22:39:22:41 | req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:23:3:23:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:23:3:23:10 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:24:3:24:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:24:3:24:12 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:25:3:25:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:25:3:25:11 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:26:3:26:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:26:3:26:9 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:27:3:27:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:27:3:27:17 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:28:3:28:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:28:3:28:9 | [ssa-synth-read] req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:28:3:28:16 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:29:3:29:5 | req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:29:3:29:12 | [ssa-synth-read] req | +| src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:29:3:29:19 | [ssa-synth-read] req | | src/express.js:22:30:32:1 | functio ... ar');\\n} | src/express.js:30:3:30:5 | req | | src/express.js:37:10:37:31 | functio ... es) { } | src/express.js:37:19:37:21 | req | | src/express.js:42:10:42:26 | (req, res) => f() | src/express.js:42:11:42:13 | req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:46:31:46:33 | req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:46:31:46:33 | req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:47:3:47:5 | req | +| src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:47:3:47:13 | [ssa-synth-read] req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:48:3:48:5 | req | +| src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:48:3:48:10 | [ssa-synth-read] req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:49:3:49:5 | req | +| src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:49:3:49:14 | [ssa-synth-read] req | | src/express.js:46:22:51:1 | functio ... ame];\\n} | src/express.js:50:3:50:5 | req | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:53:32:53:34 | req | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:53:32:53:34 | req | @@ -1766,12 +1950,16 @@ test_RouteHandler_getARequestExpr | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:5:20:5:22 | req | | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:5:20:5:22 | req | | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:6:5:6:7 | req | +| src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:6:5:6:21 | [ssa-synth-read] req | | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:7:5:7:7 | req | +| src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:7:5:7:12 | [ssa-synth-read] req | | src/middleware-flow.js:5:1:10:1 | functio ... xt();\\n} | src/middleware-flow.js:8:5:8:7 | req | | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:17:25:17:27 | req | | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:17:25:17:27 | req | | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:18:9:18:11 | req | +| src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:18:9:18:14 | [ssa-synth-read] req | | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:19:9:19:11 | req | +| src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:19:9:19:16 | [ssa-synth-read] req | | src/middleware-flow.js:17:24:21:5 | (req, r ... ;\\n } | src/middleware-flow.js:20:9:20:11 | req | | src/middleware-flow.js:23:17:23:41 | (req, r ... q.db; } | src/middleware-flow.js:23:18:23:20 | req | | src/middleware-flow.js:23:17:23:41 | (req, r ... q.db; } | src/middleware-flow.js:23:18:23:20 | req | @@ -1782,11 +1970,14 @@ test_RouteHandler_getARequestExpr | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:39:24:39:26 | req | | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:39:24:39:26 | req | | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:40:5:40:7 | req | +| src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:40:5:40:10 | [ssa-synth-read] req | | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:41:5:41:7 | req | +| src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:41:5:41:12 | [ssa-synth-read] req | | src/middleware-flow.js:39:23:43:1 | (req, r ... s.db;\\n} | src/middleware-flow.js:42:5:42:7 | req | | src/params.js:4:18:12:1 | (req, r ... }\\n} | src/params.js:4:19:4:21 | req | | src/params.js:4:18:12:1 | (req, r ... }\\n} | src/params.js:4:19:4:21 | req | | src/params.js:4:18:12:1 | (req, r ... }\\n} | src/params.js:5:17:5:19 | req | +| src/params.js:4:18:12:1 | (req, r ... }\\n} | src/params.js:5:17:5:25 | [ssa-synth-read] req | | src/params.js:4:18:12:1 | (req, r ... }\\n} | src/params.js:6:17:6:19 | req | | src/params.js:14:24:16:1 | functio ... lo");\\n} | src/params.js:14:33:14:35 | req | | src/passport.js:27:4:29:1 | functio ... ccss`\\n} | src/passport.js:27:13:27:15 | req | @@ -1892,6 +2083,8 @@ test_RouteHandler_getAResponseExpr | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:4:37:4:39 | res | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:4:37:4:39 | res | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:3:5:5 | res | +| src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:3:5:12 | [ssa-synth-read] res | +| src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:3:5:51 | [ssa-synth-read] res | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:5:3:5:51 | res.hea ... "val")) | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:6:3:6:5 | res | | src/express3.js:4:23:7:1 | functio ... al");\\n} | src/express3.js:6:3:6:17 | res.send("val") | @@ -1905,9 +2098,15 @@ test_RouteHandler_getAResponseExpr | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:4:37:4:39 | res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:4:37:4:39 | res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:3:5:5 | res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:3:5:14 | [ssa-synth-read] res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:5:3:5:35 | [ssa-synth-read] res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:6:3:6:5 | res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:6:3:6:12 | [ssa-synth-read] res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:6:3:6:45 | [ssa-synth-read] res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:6:3:6:45 | res.hea ... rget")) | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:7:3:7:5 | res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:7:3:7:12 | [ssa-synth-read] res | +| src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:7:3:7:42 | [ssa-synth-read] res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:7:3:7:42 | res.hea ... plain") | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:8:7:8:9 | res | | src/express.js:4:23:9:1 | functio ... res);\\n} | src/express.js:11:14:11:16 | arg | @@ -1932,8 +2131,12 @@ test_RouteHandler_getAResponseExpr | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:53:37:53:39 | res | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:53:37:53:39 | res | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:54:3:54:5 | res | +| src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:54:3:54:12 | [ssa-synth-read] res | +| src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:54:3:54:41 | [ssa-synth-read] res | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:54:3:54:41 | res.hea ... /html") | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:55:3:55:5 | res | +| src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:55:3:55:10 | [ssa-synth-read] res | +| src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:55:3:55:26 | [ssa-synth-read] res | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:55:3:55:26 | res.sen ... ms.foo) | | src/express.js:53:23:57:1 | functio ... res);\\n} | src/express.js:56:7:56:9 | res | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:11:14:11:16 | arg | @@ -1943,8 +2146,12 @@ test_RouteHandler_getAResponseExpr | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:59:37:59:39 | res | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:59:37:59:39 | res | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:60:3:60:5 | res | +| src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:60:3:60:12 | [ssa-synth-read] res | +| src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:60:3:60:47 | [ssa-synth-read] res | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:60:3:60:47 | res.hea ... n/xml") | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:61:3:61:5 | res | +| src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:61:3:61:10 | [ssa-synth-read] res | +| src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:61:3:61:26 | [ssa-synth-read] res | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:61:3:61:26 | res.sen ... ms.foo) | | src/express.js:59:23:63:1 | functio ... res);\\n} | src/express.js:62:7:62:9 | res | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:11:14:11:16 | arg | @@ -1954,8 +2161,12 @@ test_RouteHandler_getAResponseExpr | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:65:41:65:43 | res | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:65:41:65:43 | res | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:66:3:66:5 | res | +| src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:66:3:66:12 | [ssa-synth-read] res | +| src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:66:3:66:42 | [ssa-synth-read] res | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:66:3:66:42 | res.hea ... plain") | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:67:3:67:5 | res | +| src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:67:3:67:10 | [ssa-synth-read] res | +| src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:67:3:67:26 | [ssa-synth-read] res | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:67:3:67:26 | res.sen ... ms.foo) | | src/express.js:65:27:69:1 | functio ... res);\\n} | src/express.js:68:7:68:9 | res | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:11:14:11:16 | arg | @@ -1965,13 +2176,19 @@ test_RouteHandler_getAResponseExpr | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:71:37:71:39 | res | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:71:37:71:39 | res | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:72:3:72:5 | res | +| src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:72:3:72:12 | [ssa-synth-read] res | +| src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:72:3:72:41 | [ssa-synth-read] res | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:72:3:72:41 | res.hea ... /html") | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:73:3:73:5 | res | +| src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:73:3:73:10 | [ssa-synth-read] res | +| src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:73:3:73:20 | [ssa-synth-read] res | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:73:3:73:20 | res.send(req.path) | | src/express.js:71:23:75:1 | functio ... res);\\n} | src/express.js:74:7:74:9 | res | | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:4:29:4:31 | res | | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:4:29:4:31 | res | | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:5:2:5:4 | res | +| src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:5:2:5:8 | [ssa-synth-read] res | +| src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:5:2:5:10 | [ssa-synth-read] res | | src/inheritedFromNode.js:4:15:8:1 | functio ... .url;\\n} | src/inheritedFromNode.js:6:2:6:4 | res | | src/json.js:4:23:6:1 | functio ... ata);\\n} | src/json.js:4:37:4:39 | res | | src/json.js:4:23:6:1 | functio ... ata);\\n} | src/json.js:4:37:4:39 | res | @@ -2009,36 +2226,68 @@ test_RouteHandler_getAResponseExpr | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:16:44:16:46 | res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:16:44:16:46 | res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:19:5:19:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:19:5:19:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:19:5:19:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:19:5:19:16 | res.append() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:20:5:20:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:20:5:20:18 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:20:5:20:20 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:20:5:20:20 | res.attachment() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:21:5:21:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:21:5:21:19 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:21:5:21:21 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:21:5:21:21 | res.clearCookie() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:22:5:22:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:22:5:22:19 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:22:5:22:21 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:22:5:22:21 | res.contentType() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:23:5:23:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:23:5:23:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:23:5:23:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:23:5:23:16 | res.cookie() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:24:5:24:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:24:5:24:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:24:5:24:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:24:5:24:16 | res.format() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:25:5:25:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:25:5:25:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:25:5:25:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:25:5:25:16 | res.header() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:26:5:26:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:26:5:26:13 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:26:5:26:15 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:26:5:26:15 | res.links() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:27:5:27:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:27:5:27:16 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:27:5:27:18 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:27:5:27:18 | res.location() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:28:5:28:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:28:5:28:12 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:28:5:28:14 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:28:5:28:14 | res.send() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:29:5:29:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:29:5:29:18 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:29:5:29:20 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:29:5:29:20 | res.sendStatus() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:30:5:30:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:30:5:30:11 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:30:5:30:13 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:30:5:30:13 | res.set() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:31:5:31:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:31:5:31:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:31:5:31:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:31:5:31:16 | res.status() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:32:5:32:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:32:5:32:12 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:32:5:32:14 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:32:5:32:14 | res.type() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:33:5:33:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:33:5:33:12 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:33:5:33:14 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:33:5:33:14 | res.vary() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:35:5:35:7 | res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:35:5:35:14 | [ssa-synth-read] res | +| src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:35:5:35:16 | [ssa-synth-read] res | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:35:5:35:16 | res.status() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:35:5:35:23 | res.status().send() | | src/responseExprs.js:16:30:42:1 | functio ... }\\n} | src/responseExprs.js:37:5:37:19 | f(res.append()) | diff --git a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected index 8da9b5087438..a94560e7fff6 100644 --- a/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/NodeJSLib/tests.expected @@ -9,7 +9,9 @@ test_RequestExpr | src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:4:41:4:43 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:6:26:6:28 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:6:26:6:32 | [ssa-synth-read] req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:8:3:8:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | +| src/http.js:8:3:8:13 | [ssa-synth-read] req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:9:3:9:5 | req | src/http.js:4:32:10:1 | functio ... .foo;\\n} | | src/http.js:12:28:12:30 | req | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:55:21:55:23 | req | src/http.js:55:12:55:30 | function(req,res){} | @@ -27,18 +29,22 @@ test_RequestExpr | src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:4:42:4:44 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:6:26:6:28 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:6:26:6:32 | [ssa-synth-read] req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:8:3:8:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | +| src/https.js:8:3:8:13 | [ssa-synth-read] req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:9:3:9:5 | req | src/https.js:4:33:10:1 | functio ... .foo;\\n} | | src/https.js:12:29:12:31 | req | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:9:14:9:16 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:10:12:10:14 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:10:12:10:21 | [ssa-synth-read] req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:10:42:10:44 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:13:28:13:30 | req | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:13:28:13:30 | req | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | | src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | | src/indirect.js:16:21:16:23 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | | src/indirect.js:17:28:17:30 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | +| src/indirect.js:17:28:17:34 | [ssa-synth-read] req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | | src/indirect.js:19:33:19:35 | req | src/indirect.js:16:12:20:5 | functio ... ;\\n } | | src/indirect.js:25:25:25:27 | req | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | | src/indirect.js:28:24:28:26 | req | src/indirect.js:28:15:30:3 | functio ... ");\\n } | @@ -58,13 +64,19 @@ test_ResponseExpr | src/http.js:12:33:12:35 | res | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:12:33:12:35 | res | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:13:3:13:5 | res | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:13:3:13:15 | [ssa-synth-read] res | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:13:3:13:44 | [ssa-synth-read] res | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:14:3:14:5 | res | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:14:3:14:11 | [ssa-synth-read] res | src/http.js:12:19:16:1 | functio ... ar");\\n} | +| src/http.js:14:3:14:18 | [ssa-synth-read] res | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:15:3:15:5 | res | src/http.js:12:19:16:1 | functio ... ar");\\n} | | src/http.js:55:25:55:27 | res | src/http.js:55:12:55:30 | function(req,res){} | | src/http.js:60:27:60:29 | res | src/http.js:60:14:60:32 | function(req,res){} | | src/http.js:62:33:62:35 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:62:33:62:35 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:63:3:63:5 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:63:3:63:15 | [ssa-synth-read] res | src/http.js:62:19:65:1 | functio ... r2");\\n} | +| src/http.js:63:3:63:40 | [ssa-synth-read] res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:64:3:64:5 | res | src/http.js:62:19:65:1 | functio ... r2");\\n} | | src/http.js:68:17:68:19 | res | src/http.js:68:12:68:27 | (req,res) => f() | | src/http.js:72:34:72:36 | res | src/http.js:72:19:76:1 | functio ... \\n })\\n} | @@ -84,7 +96,11 @@ test_ResponseExpr | src/https.js:12:34:12:36 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/https.js:12:34:12:36 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/https.js:13:3:13:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/https.js:13:3:13:15 | [ssa-synth-read] res | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/https.js:13:3:13:44 | [ssa-synth-read] res | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/https.js:14:3:14:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/https.js:14:3:14:11 | [ssa-synth-read] res | src/https.js:12:20:16:1 | functio ... ar");\\n} | +| src/https.js:14:3:14:18 | [ssa-synth-read] res | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/https.js:15:3:15:5 | res | src/https.js:12:20:16:1 | functio ... ar");\\n} | | src/indirect2.js:9:19:9:21 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:9:19:9:21 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | @@ -95,6 +111,10 @@ test_ResponseExpr | src/indirect2.js:13:33:13:35 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | | src/indirect2.js:14:3:14:5 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:14:3:14:5 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | +| src/indirect2.js:14:3:14:15 | [ssa-synth-read] res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:14:3:14:15 | [ssa-synth-read] res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | +| src/indirect2.js:14:3:14:51 | [ssa-synth-read] res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | +| src/indirect2.js:14:3:14:51 | [ssa-synth-read] res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | | src/indirect2.js:15:3:15:5 | res | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | | src/indirect2.js:15:3:15:5 | res | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | | src/indirect.js:16:26:16:28 | res | src/indirect.js:16:12:20:5 | functio ... ;\\n } | @@ -285,7 +305,9 @@ test_RouteHandler_getARequestExpr | src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | | src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:4:41:4:43 | req | | src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:6:26:6:28 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:6:26:6:32 | [ssa-synth-read] req | | src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:8:3:8:5 | req | +| src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:8:3:8:13 | [ssa-synth-read] req | | src/http.js:4:32:10:1 | functio ... .foo;\\n} | src/http.js:9:3:9:5 | req | | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:28:12:30 | req | | src/http.js:55:12:55:30 | function(req,res){} | src/http.js:55:21:55:23 | req | @@ -303,18 +325,22 @@ test_RouteHandler_getARequestExpr | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:4:42:4:44 | req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:6:26:6:28 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:6:26:6:32 | [ssa-synth-read] req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:5 | req | +| src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:8:3:8:13 | [ssa-synth-read] req | | src/https.js:4:33:10:1 | functio ... .foo;\\n} | src/https.js:9:3:9:5 | req | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:29:12:31 | req | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:14:9:16 | req | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:12:10:14 | req | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:12:10:21 | [ssa-synth-read] req | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:10:42:10:44 | req | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:13:28:13:30 | req | | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:28:13:30 | req | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:21:16:23 | req | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:30 | req | +| src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:17:28:17:34 | [ssa-synth-read] req | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:19:33:19:35 | req | | src/indirect.js:25:24:27:3 | (req, r ... ");\\n } | src/indirect.js:25:25:25:27 | req | | src/indirect.js:28:15:30:3 | functio ... ");\\n } | src/indirect.js:28:24:28:26 | req | @@ -331,13 +357,19 @@ test_RouteHandler_getAResponseExpr | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:33:12:35 | res | | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:12:33:12:35 | res | | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:13:3:13:5 | res | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:13:3:13:15 | [ssa-synth-read] res | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:13:3:13:44 | [ssa-synth-read] res | | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:14:3:14:5 | res | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:14:3:14:11 | [ssa-synth-read] res | +| src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:14:3:14:18 | [ssa-synth-read] res | | src/http.js:12:19:16:1 | functio ... ar");\\n} | src/http.js:15:3:15:5 | res | | src/http.js:55:12:55:30 | function(req,res){} | src/http.js:55:25:55:27 | res | | src/http.js:60:14:60:32 | function(req,res){} | src/http.js:60:27:60:29 | res | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:33:62:35 | res | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:62:33:62:35 | res | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:3:63:5 | res | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:3:63:15 | [ssa-synth-read] res | +| src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:63:3:63:40 | [ssa-synth-read] res | | src/http.js:62:19:65:1 | functio ... r2");\\n} | src/http.js:64:3:64:5 | res | | src/http.js:68:12:68:27 | (req,res) => f() | src/http.js:68:17:68:19 | res | | src/http.js:72:19:76:1 | functio ... \\n })\\n} | src/http.js:72:34:72:36 | res | @@ -357,7 +389,11 @@ test_RouteHandler_getAResponseExpr | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:34:12:36 | res | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:12:34:12:36 | res | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:13:3:13:5 | res | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:13:3:13:15 | [ssa-synth-read] res | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:13:3:13:44 | [ssa-synth-read] res | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:14:3:14:5 | res | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:14:3:14:11 | [ssa-synth-read] res | +| src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:14:3:14:18 | [ssa-synth-read] res | | src/https.js:12:20:16:1 | functio ... ar");\\n} | src/https.js:15:3:15:5 | res | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:19:9:21 | res | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:9:19:9:21 | res | @@ -365,10 +401,14 @@ test_RouteHandler_getAResponseExpr | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:13:33:13:35 | res | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:13:33:13:35 | res | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:14:3:14:5 | res | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:14:3:14:15 | [ssa-synth-read] res | +| src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:14:3:14:51 | [ssa-synth-read] res | | src/indirect2.js:9:1:11:1 | functio ... res);\\n} | src/indirect2.js:15:3:15:5 | res | | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:33:13:35 | res | | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:13:33:13:35 | res | | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:14:3:14:5 | res | +| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:14:3:14:15 | [ssa-synth-read] res | +| src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:14:3:14:51 | [ssa-synth-read] res | | src/indirect2.js:13:1:16:1 | functio ... \\"");\\n} | src/indirect2.js:15:3:15:5 | res | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:26:16:28 | res | | src/indirect.js:16:12:20:5 | functio ... ;\\n } | src/indirect.js:16:26:16:28 | res | diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected index 16d31cd07e15..34f708288a84 100644 --- a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected @@ -175,38 +175,52 @@ reactComponentRef | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:3:11:3:10 | this | | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:4:24:4:27 | this | +| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:4:24:4:33 | [ssa-synth-read] this | | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:6:20:6:19 | this | | es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | | es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:19:11:19:10 | this | | es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:20:24:20:27 | this | +| es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:20:24:20:33 | [ssa-synth-read] this | +| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | [ssa-synth-read] this | | es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | implicit 'this' | | es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | this | | es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:2:9:2:8 | this | | es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:3:24:3:27 | this | +| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:3:24:3:33 | [ssa-synth-read] this | | es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:5:14:5:13 | this | | es6.js:14:1:20:1 | class H ... }\\n} | es6.js:15:16:15:15 | this | | es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:12 | this | +| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:18 | [ssa-synth-read] this | | es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:12 | this | +| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:18 | [ssa-synth-read] this | | es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:12 | this | +| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:31 | [ssa-synth-read] this | | exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | exportedComponent.jsx:1:8:1:7 | this | | importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | importedComponent.jsx:3:8:3:7 | this | +| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | [ssa-synth-read] this | | namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | implicit 'this' | | namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | this | +| namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | [ssa-synth-read] this | | namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | implicit 'this' | | namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | this | | plainfn.js:1:1:3:1 | functio ... div>;\\n} | plainfn.js:1:1:1:0 | this | | plainfn.js:5:1:7:1 | functio ... iv");\\n} | plainfn.js:5:1:5:0 | this | | plainfn.js:9:1:12:1 | functio ... rn x;\\n} | plainfn.js:9:1:9:0 | this | | plainfn.js:20:1:24:1 | functio ... n 42;\\n} | plainfn.js:20:1:20:0 | this | +| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | [ssa-synth-read] this | | preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | implicit 'this' | | preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | this | | preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:11:2:10 | this | +| preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | [ssa-synth-read] this | | preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | implicit 'this' | | preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | this | +| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | [ssa-synth-read] this | | probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | implicit 'this' | | probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | this | | probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:2:11:2:10 | this | | probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:12 | this | +| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:18 | [ssa-synth-read] this | +| props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | [ssa-synth-read] this | | props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | implicit 'this' | | props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | this | | props.js:2:5:3:5 | class C ... t\\n } | props.js:9:5:9:55 | new C({ ... ctor"}) | @@ -214,6 +228,7 @@ reactComponentRef | props.js:13:31:17:5 | {\\n ... }\\n } | props.js:14:24:14:23 | this | | props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:5:26:4 | this | | props.js:26:5:28:5 | functio ... ;\\n } | props.js:34:5:34:55 | new C({ ... ctor"}) | +| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | [ssa-synth-read] this | | rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | implicit 'this' | | rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | this | | rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:2:36:2:35 | this | @@ -221,29 +236,42 @@ reactComponentRef | rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:8:28:8:27 | this | | statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:2:16:2:15 | this | | statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:3:9:3:12 | this | +| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:3:9:3:18 | [ssa-synth-read] this | | statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:5:9:5:12 | this | +| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:5:9:5:18 | [ssa-synth-read] this | | statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:9:7:12 | this | +| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:9:7:21 | [ssa-synth-read] this | +| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:9:7:57 | [ssa-synth-read] this | | statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:10:23:10:22 | this | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:2:16:2:15 | this | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:3:13:3:15 | cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:3:19:3:22 | this | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:4:9:4:11 | cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:4:9:4:17 | [ssa-synth-read] cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:6:9:6:11 | cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:6:9:6:17 | [ssa-synth-read] cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:10:9:10:11 | cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:10:9:10:22 | [ssa-synth-read] cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:14:9:14:11 | cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:14:9:14:20 | [ssa-synth-read] cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:14:9:14:24 | [ssa-synth-read] cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:18:9:18:11 | cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:18:9:18:20 | [ssa-synth-read] cmp | +| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:18:9:18:30 | [ssa-synth-read] cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:22:9:22:11 | cmp | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:25:20:25:19 | this | | statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:31:5:33:6 | implicit 'this' | | statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | | statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:37:11:37:10 | this | | statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:38:24:38:27 | this | +| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:38:24:38:33 | [ssa-synth-read] this | | statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:40:20:40:19 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:2:17:2:16 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:3:9:3:12 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:5:13:5:15 | dis | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:5:19:5:22 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:6:9:6:11 | dis | +| thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:8:9:10:21 | [ssa-synth-read] this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:8:10:8:9 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:9:13:9:16 | this | | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:10:17:10:20 | this | @@ -251,32 +279,41 @@ reactComponentRef | thisAccesses.js:1:1:16:1 | class C ... }\\n} | thisAccesses.js:14:9:14:12 | this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:19:13:19:12 | this | +| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:20:9:22:21 | [ssa-synth-read] this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:20:10:20:9 | this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:21:13:21:16 | this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:22:17:22:20 | this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:26:25:26:24 | this | | thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | thisAccesses.js:27:9:27:12 | this | | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:31:2:31:1 | this | +| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:32:5:34:17 | [ssa-synth-read] this | | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:32:6:32:5 | this | | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:33:9:33:12 | this | | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:34:13:34:16 | this | | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:39:13:39:12 | this | +| thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:40:9:42:16 | [ssa-synth-read] this | | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:40:38:40:37 | this | | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:41:13:41:16 | this | | thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | thisAccesses.js:42:12:42:15 | this | | thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:17:48:16 | this | | thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:9:49:12 | this | +| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:9:49:22 | [ssa-synth-read] this | | thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:50:9:50:12 | this | +| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:50:9:50:22 | [ssa-synth-read] this | | thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:55:16:55:15 | this | | thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:59:11:59:10 | this | | thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:60:20:60:23 | this | +| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:60:20:60:28 | [ssa-synth-read] this | | thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:61:20:61:23 | this | +| thisAccesses.js:54:1:63:1 | class C ... }\\n} | thisAccesses.js:61:20:61:28 | [ssa-synth-read] this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:5:13:5:12 | this | +| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:6:9:8:16 | [ssa-synth-read] this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:6:38:6:37 | this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:7:13:7:16 | this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:8:12:8:15 | this | +| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:9:9:11:16 | [ssa-synth-read] this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:9:25:9:24 | this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:10:13:10:16 | this | | thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:11:12:11:15 | this | diff --git a/javascript/ql/test/library-tests/frameworks/fastify/tests.expected b/javascript/ql/test/library-tests/frameworks/fastify/tests.expected index e2b3b303a6ea..e87d73cb4214 100644 --- a/javascript/ql/test/library-tests/frameworks/fastify/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/fastify/tests.expected @@ -105,28 +105,39 @@ test_RouteHandler_getARequestExpr | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:34:26:34:32 | request | | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:34:26:34:32 | request | | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:36:5:36:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:36:5:36:17 | [ssa-synth-read] request | | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:37:5:37:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:37:5:37:16 | [ssa-synth-read] request | | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:38:5:38:11 | request | +| src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:38:5:38:18 | [ssa-synth-read] request | | src/fastify.js:34:17:46:3 | functio ... eam\\n } | src/fastify.js:39:5:39:11 | request | | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:54:26:54:32 | request | | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:54:26:54:32 | request | | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:55:5:55:11 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:55:5:55:17 | [ssa-synth-read] request | | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:56:5:56:11 | request | +| src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:56:5:56:16 | [ssa-synth-read] request | | src/fastify.js:54:17:58:3 | functio ... ms;\\n } | src/fastify.js:57:5:57:11 | request | | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:65:26:65:32 | request | | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:65:26:65:32 | request | | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:66:5:66:11 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:66:5:66:17 | [ssa-synth-read] request | | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:67:5:67:11 | request | +| src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:67:5:67:16 | [ssa-synth-read] request | | src/fastify.js:65:17:69:3 | functio ... ms;\\n } | src/fastify.js:68:5:68:11 | request | | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:76:26:76:32 | request | | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:76:26:76:32 | request | | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:77:5:77:11 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:77:5:77:17 | [ssa-synth-read] request | | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:78:5:78:11 | request | +| src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:78:5:78:16 | [ssa-synth-read] request | | src/fastify.js:76:17:80:3 | functio ... ms;\\n } | src/fastify.js:79:5:79:11 | request | | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:87:26:87:32 | request | | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:87:26:87:32 | request | | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:88:5:88:11 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:88:5:88:17 | [ssa-synth-read] request | | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:89:5:89:11 | request | +| src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:89:5:89:16 | [ssa-synth-read] request | | src/fastify.js:87:17:91:3 | functio ... ms;\\n } | src/fastify.js:90:5:90:11 | request | | src/fastify.js:99:17:101:3 | (reques ... nse\\n } | src/fastify.js:99:18:99:24 | request | test_HeaderDefinition_getAHeaderName diff --git a/javascript/ql/test/library-tests/frameworks/hapi/tests.expected b/javascript/ql/test/library-tests/frameworks/hapi/tests.expected index 6d53a86165f8..6b1ef45f9661 100644 --- a/javascript/ql/test/library-tests/frameworks/hapi/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/hapi/tests.expected @@ -23,14 +23,20 @@ test_RequestExpr | src/hapi.js:20:19:20:25 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:20:19:20:25 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:21:3:21:9 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:21:3:21:20 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:22:3:22:21 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:22:3:22:21 | getRequest(request) | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:22:14:22:20 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:23:3:23:21 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:23:3:23:21 | getRequest(request) | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:23:14:23:20 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:23:34:23:52 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:23:34:23:52 | getRequest(request) | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:23:45:23:51 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:24:3:24:9 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:24:3:24:13 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:25:3:25:9 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapi.js:25:3:25:17 | [ssa-synth-read] request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:26:3:26:9 | request | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapi.js:34:22:34:24 | req | src/hapi.js:34:12:34:30 | function (req, h){} | | src/hapi.js:38:1:40:1 | return of function getRequest | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | @@ -44,12 +50,19 @@ test_RequestExpr | src/hapiglue.js:20:19:20:25 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:20:19:20:25 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:21:3:21:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:21:3:21:20 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:22:3:22:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:22:3:22:17 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:23:3:23:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:23:3:23:15 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:24:3:24:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:24:3:24:16 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:25:3:25:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:25:3:25:13 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:26:3:26:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:26:3:26:13 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:27:3:27:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | +| src/hapiglue.js:27:3:27:17 | [ssa-synth-read] request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:28:3:28:9 | request | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | | src/hapiglue.js:36:22:36:24 | req | src/hapiglue.js:36:12:36:33 | functio ... hapi){} | | src/hapihapi.js:13:32:13:38 | request | src/hapihapi.js:13:14:15:5 | functio ... n\\n } | @@ -59,16 +72,23 @@ test_RequestExpr | src/hapihapi.js:20:19:20:25 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:20:19:20:25 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:21:3:21:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapihapi.js:21:3:21:20 | [ssa-synth-read] request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:22:3:22:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapihapi.js:22:3:22:17 | [ssa-synth-read] request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:23:3:23:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapihapi.js:23:3:23:15 | [ssa-synth-read] request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:24:3:24:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapihapi.js:24:3:24:13 | [ssa-synth-read] request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:25:3:25:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | +| src/hapihapi.js:25:3:25:17 | [ssa-synth-read] request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:26:3:26:9 | request | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | | src/hapihapi.js:34:22:34:24 | req | src/hapihapi.js:34:12:34:30 | function (req, h){} | | src/wrapped-route-configurator.js:8:39:8:45 | request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | | src/wrapped-route-configurator.js:8:39:8:45 | request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | | src/wrapped-route-configurator.js:9:20:9:26 | request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | +| src/wrapped-route-configurator.js:9:20:9:33 | [ssa-synth-read] request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | | src/wrapped-route-configurator.js:9:36:9:42 | request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | +| src/wrapped-route-configurator.js:9:36:9:48 | [ssa-synth-read] request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | | src/wrapped-route-configurator.js:9:51:9:57 | request | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | test_HeaderAccess | src/hapi.js:25:3:25:21 | request.headers.baz | baz | @@ -186,14 +206,20 @@ test_RouteHandler_getARequestExpr | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:20:19:20:25 | request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:20:19:20:25 | request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:21:3:21:9 | request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:21:3:21:20 | [ssa-synth-read] request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:22:3:22:21 | [ssa-synth-read] request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:22:3:22:21 | getRequest(request) | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:22:14:22:20 | request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:3:23:21 | [ssa-synth-read] request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:3:23:21 | getRequest(request) | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:14:23:20 | request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:34:23:52 | [ssa-synth-read] request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:34:23:52 | getRequest(request) | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:23:45:23:51 | request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:24:3:24:9 | request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:24:3:24:13 | [ssa-synth-read] request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:25:3:25:9 | request | +| src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:25:3:25:17 | [ssa-synth-read] request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:26:3:26:9 | request | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:38:1:40:1 | return of function getRequest | | src/hapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapi.js:38:21:38:27 | request | @@ -207,12 +233,19 @@ test_RouteHandler_getARequestExpr | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:20:19:20:25 | request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:20:19:20:25 | request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:21:3:21:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:21:3:21:20 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:22:3:22:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:22:3:22:17 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:23:3:23:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:23:3:23:15 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:24:3:24:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:24:3:24:16 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:25:3:25:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:25:3:25:13 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:26:3:26:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:26:3:26:13 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:27:3:27:9 | request | +| src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:27:3:27:17 | [ssa-synth-read] request | | src/hapiglue.js:20:1:29:1 | functio ... oken;\\n} | src/hapiglue.js:28:3:28:9 | request | | src/hapiglue.js:36:12:36:33 | functio ... hapi){} | src/hapiglue.js:36:22:36:24 | req | | src/hapihapi.js:13:14:15:5 | functio ... n\\n } | src/hapihapi.js:13:32:13:38 | request | @@ -222,16 +255,23 @@ test_RouteHandler_getARequestExpr | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:20:19:20:25 | request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:20:19:20:25 | request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:21:3:21:9 | request | +| src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:21:3:21:20 | [ssa-synth-read] request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:22:3:22:9 | request | +| src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:22:3:22:17 | [ssa-synth-read] request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:23:3:23:9 | request | +| src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:23:3:23:15 | [ssa-synth-read] request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:24:3:24:9 | request | +| src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:24:3:24:13 | [ssa-synth-read] request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:25:3:25:9 | request | +| src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:25:3:25:17 | [ssa-synth-read] request | | src/hapihapi.js:20:1:27:1 | functio ... oken;\\n} | src/hapihapi.js:26:3:26:9 | request | | src/hapihapi.js:34:12:34:30 | function (req, h){} | src/hapihapi.js:34:22:34:24 | req | | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:8:39:8:45 | request | | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:8:39:8:45 | request | | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:9:20:9:26 | request | +| src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:9:20:9:33 | [ssa-synth-read] request | | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:9:36:9:42 | request | +| src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:9:36:9:48 | [ssa-synth-read] request | | src/wrapped-route-configurator.js:8:23:10:3 | async f ... d);\\n } | src/wrapped-route-configurator.js:9:51:9:57 | request | test_HeaderDefinition_getAHeaderName | src/hapi.js:14:9:14:46 | request ... 1', '') | header1 | diff --git a/javascript/ql/test/library-tests/frameworks/koa/tests.expected b/javascript/ql/test/library-tests/frameworks/koa/tests.expected index 1c3a323d1105..570be491613d 100644 --- a/javascript/ql/test/library-tests/frameworks/koa/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/koa/tests.expected @@ -10,42 +10,76 @@ test_ContextExpr | src/koa.js:10:28:10:30 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:10:28:10:30 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:11:3:11:6 | this | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:11:3:11:10 | [ssa-synth-read] this | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:11:3:11:25 | [ssa-synth-read] this | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:12:3:12:6 | this | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:12:3:12:15 | [ssa-synth-read] this | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:13:3:13:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:13:3:13:9 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:13:3:13:24 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:14:3:14:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:14:3:14:14 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:15:13:15:15 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:15:13:15:24 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:18:3:18:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:18:3:18:14 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:19:3:19:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:19:3:19:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:20:3:20:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:20:3:20:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:21:3:21:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:21:3:21:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:22:3:22:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:22:3:22:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:23:3:23:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:23:3:23:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:24:3:24:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:24:3:24:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:25:3:25:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:25:3:25:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:26:3:26:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | +| src/koa.js:26:3:26:13 | [ssa-synth-read] ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:27:3:27:5 | ctx | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:30:16:30:18 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:30:16:30:18 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:31:2:31:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:31:2:31:13 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:32:2:32:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:32:2:32:9 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:33:2:33:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:33:2:33:10 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:34:2:34:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:34:2:34:8 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:35:2:35:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:35:2:35:16 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:36:2:36:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:36:2:36:9 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:37:2:37:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:37:2:37:11 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:38:2:38:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:38:2:38:12 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:39:2:39:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:39:2:39:8 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:39:2:39:15 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:40:2:40:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:40:2:40:8 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:40:2:40:15 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:42:12:42:14 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:42:12:42:20 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:43:2:43:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:43:2:43:13 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | +| src/koa.js:43:2:43:18 | [ssa-synth-read] ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:44:2:44:4 | ctx | src/koa.js:30:10:45:1 | async c ... url);\\n} | | src/koa.js:47:16:47:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | | src/koa.js:47:16:47:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | | src/koa.js:48:16:48:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | +| src/koa.js:48:16:48:26 | [ssa-synth-read] ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | | src/koa.js:51:14:51:16 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | +| src/koa.js:51:14:51:22 | [ssa-synth-read] ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | | src/koa.js:54:16:54:18 | ctx | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | | src/koa.js:59:10:59:9 | this | src/koa.js:59:10:61:1 | functio ... .url;\\n} | | src/koa.js:60:2:60:5 | this | src/koa.js:59:10:61:1 | functio ... .url;\\n} | +| src/koa.js:60:2:60:13 | [ssa-synth-read] this | src/koa.js:59:10:61:1 | functio ... .url;\\n} | test_RequestExpr | src/koa.js:19:3:19:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} | | src/koa.js:20:3:20:13 | ctx.request | src/koa.js:10:10:28:1 | functio ... az');\\n} | @@ -141,42 +175,76 @@ test_RouteHandler_getAContextExpr | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:10:28:10:30 | ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:10:28:10:30 | ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:11:3:11:6 | this | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:11:3:11:10 | [ssa-synth-read] this | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:11:3:11:25 | [ssa-synth-read] this | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:6 | this | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:12:3:12:15 | [ssa-synth-read] this | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:13:3:13:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:13:3:13:9 | [ssa-synth-read] ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:13:3:13:24 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:14:3:14:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:14:3:14:14 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:15:13:15:15 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:15:13:15:24 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:18:3:18:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:18:3:18:14 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:19:3:19:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:19:3:19:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:20:3:20:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:20:3:20:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:21:3:21:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:21:3:21:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:22:3:22:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:22:3:22:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:23:3:23:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:23:3:23:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:24:3:24:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:24:3:24:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:25:3:25:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:25:3:25:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:5 | ctx | +| src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:26:3:26:13 | [ssa-synth-read] ctx | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:27:3:27:5 | ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:30:16:30:18 | ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:30:16:30:18 | ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:31:2:31:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:31:2:31:13 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:32:2:32:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:32:2:32:9 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:33:2:33:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:33:2:33:10 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:34:2:34:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:34:2:34:8 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:35:2:35:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:35:2:35:16 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:36:2:36:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:36:2:36:9 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:37:2:37:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:37:2:37:11 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:38:2:38:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:38:2:38:12 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:39:2:39:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:39:2:39:8 | [ssa-synth-read] ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:39:2:39:15 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:40:2:40:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:40:2:40:8 | [ssa-synth-read] ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:40:2:40:15 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:42:12:42:14 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:42:12:42:20 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:43:2:43:4 | ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:43:2:43:13 | [ssa-synth-read] ctx | +| src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:43:2:43:18 | [ssa-synth-read] ctx | | src/koa.js:30:10:45:1 | async c ... url);\\n} | src/koa.js:44:2:44:4 | ctx | | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:47:16:47:18 | ctx | | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:47:16:47:18 | ctx | | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:48:16:48:18 | ctx | +| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:48:16:48:26 | [ssa-synth-read] ctx | | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:51:14:51:16 | ctx | +| src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:51:14:51:22 | [ssa-synth-read] ctx | | src/koa.js:47:10:56:1 | async c ... .foo;\\n} | src/koa.js:54:16:54:18 | ctx | | src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:59:10:59:9 | this | | src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:5 | this | +| src/koa.js:59:10:61:1 | functio ... .url;\\n} | src/koa.js:60:2:60:13 | [ssa-synth-read] this | test_RouteHandler_getARequestExpr | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:19:3:19:13 | ctx.request | | src/koa.js:10:10:28:1 | functio ... az');\\n} | src/koa.js:20:3:20:13 | ctx.request | diff --git a/javascript/ql/test/library-tests/frameworks/restify/tests.expected b/javascript/ql/test/library-tests/frameworks/restify/tests.expected index 7cc5f1a2770d..aebac4192988 100644 --- a/javascript/ql/test/library-tests/frameworks/restify/tests.expected +++ b/javascript/ql/test/library-tests/frameworks/restify/tests.expected @@ -7,12 +7,26 @@ test_RequestExpr | src/test.js:12:37:12:43 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:12:37:12:43 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:14:5:14:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:14:5:14:20 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:14:5:14:22 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:15:5:15:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:15:5:15:16 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:15:5:15:18 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:16:5:16:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:16:5:16:19 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:16:5:16:21 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:17:5:17:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:17:5:17:26 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:17:5:17:28 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:18:5:18:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:18:5:18:21 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:18:5:18:23 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:19:5:19:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:19:5:19:19 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:19:5:19:26 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:20:5:20:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:20:5:20:18 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | +| src/test.js:20:5:20:25 | [ssa-synth-read] request | src/test.js:12:19:22:1 | functio ... okie;\\n} | | src/test.js:21:5:21:11 | request | src/test.js:12:19:22:1 | functio ... okie;\\n} | test_ResponseExpr | src/test.js:9:46:9:53 | response | src/test.js:9:19:11:1 | functio ... ition\\n} | @@ -61,12 +75,26 @@ test_RouteHandler_getARequestExpr | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:12:37:12:43 | request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:12:37:12:43 | request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:14:5:14:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:14:5:14:20 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:14:5:14:22 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:15:5:15:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:15:5:15:16 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:15:5:15:18 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:16:5:16:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:16:5:16:19 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:16:5:16:21 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:17:5:17:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:17:5:17:26 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:17:5:17:28 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:18:5:18:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:18:5:18:21 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:18:5:18:23 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:19:5:19:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:19:5:19:19 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:19:5:19:26 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:20:5:20:11 | request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:20:5:20:18 | [ssa-synth-read] request | +| src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:20:5:20:25 | [ssa-synth-read] request | | src/test.js:12:19:22:1 | functio ... okie;\\n} | src/test.js:21:5:21:11 | request | test_RouteHandler_getAResponseExpr | src/test.js:9:19:11:1 | functio ... ition\\n} | src/test.js:9:46:9:53 | response | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected index 3f854c95b7d8..a1082b3f58fa 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/TaintedPath.expected @@ -127,7 +127,6 @@ | normalizedPaths.js:194:21:194:24 | path | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:194:21:194:24 | path | This path depends on a $@. | normalizedPaths.js:174:14:174:27 | req.query.path | user-provided value | | normalizedPaths.js:199:21:199:24 | path | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:199:21:199:24 | path | This path depends on a $@. | normalizedPaths.js:174:14:174:27 | req.query.path | user-provided value | | normalizedPaths.js:205:21:205:34 | normalizedPath | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:205:21:205:34 | normalizedPath | This path depends on a $@. | normalizedPaths.js:174:14:174:27 | req.query.path | user-provided value | -| normalizedPaths.js:208:21:208:34 | normalizedPath | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:208:21:208:34 | normalizedPath | This path depends on a $@. | normalizedPaths.js:174:14:174:27 | req.query.path | user-provided value | | normalizedPaths.js:210:21:210:34 | normalizedPath | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:210:21:210:34 | normalizedPath | This path depends on a $@. | normalizedPaths.js:174:14:174:27 | req.query.path | user-provided value | | normalizedPaths.js:222:21:222:24 | path | normalizedPaths.js:214:35:214:48 | req.query.path | normalizedPaths.js:222:21:222:24 | path | This path depends on a $@. | normalizedPaths.js:214:35:214:48 | req.query.path | user-provided value | | normalizedPaths.js:228:21:228:24 | path | normalizedPaths.js:226:35:226:48 | req.query.path | normalizedPaths.js:228:21:228:24 | path | This path depends on a $@. | normalizedPaths.js:226:35:226:48 | req.query.path | user-provided value | @@ -535,7 +534,6 @@ edges | normalizedPaths.js:174:7:174:10 | path | normalizedPaths.js:201:45:201:48 | path | provenance | | | normalizedPaths.js:174:14:174:27 | req.query.path | normalizedPaths.js:174:7:174:10 | path | provenance | | | normalizedPaths.js:201:7:201:20 | normalizedPath | normalizedPaths.js:205:21:205:34 | normalizedPath | provenance | | -| normalizedPaths.js:201:7:201:20 | normalizedPath | normalizedPaths.js:208:21:208:34 | normalizedPath | provenance | | | normalizedPaths.js:201:7:201:20 | normalizedPath | normalizedPaths.js:210:21:210:34 | normalizedPath | provenance | | | normalizedPaths.js:201:24:201:49 | pathMod ... e(path) | normalizedPaths.js:201:7:201:20 | normalizedPath | provenance | | | normalizedPaths.js:201:45:201:48 | path | normalizedPaths.js:201:24:201:49 | pathMod ... e(path) | provenance | Config | @@ -1116,7 +1114,6 @@ nodes | normalizedPaths.js:201:24:201:49 | pathMod ... e(path) | semmle.label | pathMod ... e(path) | | normalizedPaths.js:201:45:201:48 | path | semmle.label | path | | normalizedPaths.js:205:21:205:34 | normalizedPath | semmle.label | normalizedPath | -| normalizedPaths.js:208:21:208:34 | normalizedPath | semmle.label | normalizedPath | | normalizedPaths.js:210:21:210:34 | normalizedPath | semmle.label | normalizedPath | | normalizedPaths.js:214:7:214:10 | path | semmle.label | path | | normalizedPaths.js:214:14:214:49 | pathMod ... y.path) | semmle.label | pathMod ... y.path) | diff --git a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js index 4365de77a40f..8588ffbb1eae 100644 --- a/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js +++ b/javascript/ql/test/query-tests/Security/CWE-022/TaintedPath/normalizedPaths.js @@ -205,7 +205,7 @@ app.get('/join-regression', (req, res) => { fs.readFileSync(normalizedPath); // $ Alert if (normalizedPath.startsWith('/home/user/www') || normalizedPath.startsWith('/home/user/public')) - fs.readFileSync(normalizedPath); // $ SPURIOUS: Alert + fs.readFileSync(normalizedPath); else fs.readFileSync(normalizedPath); // $ Alert }); diff --git a/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/SecondOrderCommandInjection.expected b/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/SecondOrderCommandInjection.expected index 85d629191c5a..21435b065827 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/SecondOrderCommandInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/SecondOrderCommandInjection.expected @@ -3,19 +3,20 @@ | second-order.js:9:29:9:34 | remote | second-order.js:6:18:6:33 | req.query.remote | second-order.js:9:29:9:34 | remote | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:6:18:6:33 | req.query.remote | a user-provided value | | second-order.js:11:33:11:38 | remote | second-order.js:6:18:6:33 | req.query.remote | second-order.js:11:33:11:38 | remote | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:6:18:6:33 | req.query.remote | a user-provided value | | second-order.js:15:19:15:24 | myArgs | second-order.js:13:18:13:31 | req.query.args | second-order.js:15:19:15:24 | myArgs | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:13:18:13:31 | req.query.args | a user-provided value | -| second-order.js:26:35:26:40 | remote | second-order.js:6:18:6:33 | req.query.remote | second-order.js:26:35:26:40 | remote | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:6:18:6:33 | req.query.remote | a user-provided value | -| second-order.js:29:19:29:32 | req.query.args | second-order.js:29:19:29:32 | req.query.args | second-order.js:29:19:29:32 | req.query.args | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:29:19:29:32 | req.query.args | a user-provided value | -| second-order.js:40:28:40:43 | req.query.remote | second-order.js:40:28:40:43 | req.query.remote | second-order.js:40:28:40:43 | req.query.remote | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:40:28:40:43 | req.query.remote | a user-provided value | -| second-order.js:42:31:42:46 | req.query.remote | second-order.js:42:31:42:46 | req.query.remote | second-order.js:42:31:42:46 | req.query.remote | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:42:31:42:46 | req.query.remote | a user-provided value | -| second-order.js:44:18:44:31 | req.query.args | second-order.js:44:18:44:31 | req.query.args | second-order.js:44:18:44:31 | req.query.args | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:44:18:44:31 | req.query.args | a user-provided value | +| second-order.js:29:35:29:49 | uncheckedRemote | second-order.js:25:27:25:51 | req.que ... dRemote | second-order.js:29:35:29:49 | uncheckedRemote | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:25:27:25:51 | req.que ... dRemote | a user-provided value | +| second-order.js:32:19:32:32 | req.query.args | second-order.js:32:19:32:32 | req.query.args | second-order.js:32:19:32:32 | req.query.args | Command line argument that depends on $@ can execute an arbitrary command if --upload-pack is used with git. | second-order.js:32:19:32:32 | req.query.args | a user-provided value | +| second-order.js:43:28:43:43 | req.query.remote | second-order.js:43:28:43:43 | req.query.remote | second-order.js:43:28:43:43 | req.query.remote | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:43:28:43:43 | req.query.remote | a user-provided value | +| second-order.js:45:31:45:46 | req.query.remote | second-order.js:45:31:45:46 | req.query.remote | second-order.js:45:31:45:46 | req.query.remote | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:45:31:45:46 | req.query.remote | a user-provided value | +| second-order.js:47:18:47:31 | req.query.args | second-order.js:47:18:47:31 | req.query.args | second-order.js:47:18:47:31 | req.query.args | Command line argument that depends on $@ can execute an arbitrary command if --config=alias.= is used with hg. | second-order.js:47:18:47:31 | req.query.args | a user-provided value | edges | second-order.js:6:9:6:14 | remote | second-order.js:7:33:7:38 | remote | provenance | | | second-order.js:6:9:6:14 | remote | second-order.js:9:29:9:34 | remote | provenance | | | second-order.js:6:9:6:14 | remote | second-order.js:11:33:11:38 | remote | provenance | | -| second-order.js:6:9:6:14 | remote | second-order.js:26:35:26:40 | remote | provenance | | | second-order.js:6:18:6:33 | req.query.remote | second-order.js:6:9:6:14 | remote | provenance | | | second-order.js:13:9:13:14 | myArgs | second-order.js:15:19:15:24 | myArgs | provenance | | | second-order.js:13:18:13:31 | req.query.args | second-order.js:13:9:13:14 | myArgs | provenance | | +| second-order.js:25:9:25:23 | uncheckedRemote | second-order.js:29:35:29:49 | uncheckedRemote | provenance | | +| second-order.js:25:27:25:51 | req.que ... dRemote | second-order.js:25:9:25:23 | uncheckedRemote | provenance | | nodes | second-order.js:6:9:6:14 | remote | semmle.label | remote | | second-order.js:6:18:6:33 | req.query.remote | semmle.label | req.query.remote | @@ -25,9 +26,11 @@ nodes | second-order.js:13:9:13:14 | myArgs | semmle.label | myArgs | | second-order.js:13:18:13:31 | req.query.args | semmle.label | req.query.args | | second-order.js:15:19:15:24 | myArgs | semmle.label | myArgs | -| second-order.js:26:35:26:40 | remote | semmle.label | remote | -| second-order.js:29:19:29:32 | req.query.args | semmle.label | req.query.args | -| second-order.js:40:28:40:43 | req.query.remote | semmle.label | req.query.remote | -| second-order.js:42:31:42:46 | req.query.remote | semmle.label | req.query.remote | -| second-order.js:44:18:44:31 | req.query.args | semmle.label | req.query.args | +| second-order.js:25:9:25:23 | uncheckedRemote | semmle.label | uncheckedRemote | +| second-order.js:25:27:25:51 | req.que ... dRemote | semmle.label | req.que ... dRemote | +| second-order.js:29:35:29:49 | uncheckedRemote | semmle.label | uncheckedRemote | +| second-order.js:32:19:32:32 | req.query.args | semmle.label | req.query.args | +| second-order.js:43:28:43:43 | req.query.remote | semmle.label | req.query.remote | +| second-order.js:45:31:45:46 | req.query.remote | semmle.label | req.query.remote | +| second-order.js:47:18:47:31 | req.query.args | semmle.label | req.query.args | subpaths diff --git a/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/second-order.js b/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/second-order.js index b49d6b2bd73b..4656afd50713 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/second-order.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/SecondOrderCommandInjection/second-order.js @@ -20,10 +20,13 @@ app.get("/", (req, res) => { execFile("git", ["ls-remote", remote, "HEAD"]); // OK - it's not an option } - if (remote.startsWith("git@")) { - execFile("git", ["ls-remote", remote, "HEAD"]); // OK - it's a git URL + execFile("git", ["ls-remote", remote, "HEAD"]); // Both outcomes of the earlier check are modeled as safe. + + const uncheckedRemote = req.query.uncheckedRemote; // $ Source + if (uncheckedRemote.startsWith("git@")) { + execFile("git", ["ls-remote", uncheckedRemote, "HEAD"]); // OK - it's a git URL } else { - execFile("git", ["ls-remote", remote, "HEAD"]); // $ Alert - unknown starting string + execFile("git", ["ls-remote", uncheckedRemote, "HEAD"]); // $ Alert - unknown starting string } execFile("git", req.query.args); // $ Alert - unknown args diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected index 564d9ca221e7..c077a32d9218 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/UnsafeShellCommandConstruction.expected @@ -47,7 +47,6 @@ | lib/lib.js:228:10:228:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:227:39:227:42 | name | library input | lib/lib.js:228:2:228:26 | cp.exec ... + name) | shell command | | lib/lib.js:236:10:236:25 | "rm -rf " + name | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:227:39:227:42 | name | library input | lib/lib.js:236:2:236:26 | cp.exec ... + name) | shell command | | lib/lib.js:249:10:249:25 | "rm -rf " + name | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:248:42:248:45 | name | library input | lib/lib.js:249:2:249:26 | cp.exec ... + name) | shell command | -| lib/lib.js:253:10:253:28 | "rm -rf " + cleaned | lib/lib.js:248:42:248:45 | name | lib/lib.js:253:22:253:28 | cleaned | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:248:42:248:45 | name | library input | lib/lib.js:253:2:253:29 | cp.exec ... leaned) | shell command | | lib/lib.js:258:10:258:25 | "rm -rf " + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:257:35:257:38 | name | library input | lib/lib.js:258:2:258:26 | cp.exec ... + name) | shell command | | lib/lib.js:261:11:261:33 | "rm -rf ... + name | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:257:35:257:38 | name | library input | lib/lib.js:261:3:261:34 | cp.exec ... + name) | shell command | | lib/lib.js:268:10:268:32 | "rm -rf ... version | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:32 | obj.version | This string concatenation which depends on $@ is later used in a $@. | lib/lib.js:267:46:267:48 | obj | library input | lib/lib.js:268:2:268:33 | cp.exec ... ersion) | shell command | @@ -157,13 +156,7 @@ edges | lib/lib.js:216:39:216:42 | name | lib/lib.js:224:22:224:25 | name | provenance | | | lib/lib.js:227:39:227:42 | name | lib/lib.js:228:22:228:25 | name | provenance | | | lib/lib.js:227:39:227:42 | name | lib/lib.js:236:22:236:25 | name | provenance | | -| lib/lib.js:239:28:239:28 | s | lib/lib.js:245:9:245:9 | s | provenance | | | lib/lib.js:248:42:248:45 | name | lib/lib.js:249:22:249:25 | name | provenance | | -| lib/lib.js:248:42:248:45 | name | lib/lib.js:251:27:251:30 | name | provenance | | -| lib/lib.js:251:6:251:12 | cleaned | lib/lib.js:253:22:253:28 | cleaned | provenance | | -| lib/lib.js:251:16:251:31 | cleanInput(name) | lib/lib.js:251:6:251:12 | cleaned | provenance | | -| lib/lib.js:251:27:251:30 | name | lib/lib.js:239:28:239:28 | s | provenance | | -| lib/lib.js:251:27:251:30 | name | lib/lib.js:251:16:251:31 | cleanInput(name) | provenance | | | lib/lib.js:257:35:257:38 | name | lib/lib.js:258:22:258:25 | name | provenance | | | lib/lib.js:257:35:257:38 | name | lib/lib.js:261:30:261:33 | name | provenance | | | lib/lib.js:267:46:267:48 | obj | lib/lib.js:268:22:268:24 | obj | provenance | | @@ -329,14 +322,8 @@ nodes | lib/lib.js:227:39:227:42 | name | semmle.label | name | | lib/lib.js:228:22:228:25 | name | semmle.label | name | | lib/lib.js:236:22:236:25 | name | semmle.label | name | -| lib/lib.js:239:28:239:28 | s | semmle.label | s | -| lib/lib.js:245:9:245:9 | s | semmle.label | s | | lib/lib.js:248:42:248:45 | name | semmle.label | name | | lib/lib.js:249:22:249:25 | name | semmle.label | name | -| lib/lib.js:251:6:251:12 | cleaned | semmle.label | cleaned | -| lib/lib.js:251:16:251:31 | cleanInput(name) | semmle.label | cleanInput(name) | -| lib/lib.js:251:27:251:30 | name | semmle.label | name | -| lib/lib.js:253:22:253:28 | cleaned | semmle.label | cleaned | | lib/lib.js:257:35:257:38 | name | semmle.label | name | | lib/lib.js:258:22:258:25 | name | semmle.label | name | | lib/lib.js:261:30:261:33 | name | semmle.label | name | @@ -452,6 +439,5 @@ nodes | lib/subLib/index.js:13:44:13:46 | arr | semmle.label | arr | | lib/subLib/index.js:14:22:14:24 | arr | semmle.label | arr | subpaths -| lib/lib.js:251:27:251:30 | name | lib/lib.js:239:28:239:28 | s | lib/lib.js:245:9:245:9 | s | lib/lib.js:251:16:251:31 | cleanInput(name) | | lib/lib.js:340:25:340:25 | n | lib/lib.js:329:13:329:13 | x | lib/lib.js:330:9:330:9 | x | lib/lib.js:340:22:340:26 | id(n) | | lib/lib.js:428:28:428:57 | (name ? ... ) + '-' | lib/lib.js:431:23:431:26 | last | lib/lib.js:437:9:437:11 | arr [ArrayElement] | lib/lib.js:428:14:428:58 | build(" ... + '-') | diff --git a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js index e7911746d6eb..e6e93b5bc433 100644 --- a/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js +++ b/javascript/ql/test/query-tests/Security/CWE-078/UnsafeShellCommandConstruction/lib/lib.js @@ -250,7 +250,7 @@ module.exports.goodSanitizer = function (name) { // $ Source var cleaned = cleanInput(name); - cp.exec("rm -rf " + cleaned); // $ SPURIOUS: Alert - SanitizingRegExpTest is not able to generate a barrier edge for an edge into a phi node. + cp.exec("rm -rf " + cleaned); } var fs = require("fs"); diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected index fce55fec2936..866bc99a2ee3 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/Xss.expected @@ -702,10 +702,8 @@ edges | tst.js:375:18:375:23 | target [taint3] | tst.js:375:18:375:30 | target.taint3 | provenance | | | tst.js:380:18:380:23 | target | tst.js:380:18:380:30 | target.taint5 | provenance | | | tst.js:389:18:389:23 | target | tst.js:389:18:389:30 | target.taint7 | provenance | | -| tst.js:391:3:391:8 | [post update] target [taint8] | tst.js:391:19:391:24 | target [taint8] | provenance | | | tst.js:391:3:391:8 | [post update] target [taint8] | tst.js:392:18:392:23 | target [taint8] | provenance | | | tst.js:391:19:391:24 | target | tst.js:391:19:391:31 | target.taint8 | provenance | | -| tst.js:391:19:391:24 | target [taint8] | tst.js:391:19:391:31 | target.taint8 | provenance | | | tst.js:391:19:391:31 | target.taint8 | tst.js:391:3:391:8 | [post update] target [taint8] | provenance | | | tst.js:392:18:392:23 | target [taint8] | tst.js:392:18:392:30 | target.taint8 | provenance | | | tst.js:399:7:399:13 | payload | tst.js:400:18:400:24 | payload | provenance | | @@ -1351,7 +1349,6 @@ nodes | tst.js:389:18:389:30 | target.taint7 | semmle.label | target.taint7 | | tst.js:391:3:391:8 | [post update] target [taint8] | semmle.label | [post update] target [taint8] | | tst.js:391:19:391:24 | target | semmle.label | target | -| tst.js:391:19:391:24 | target [taint8] | semmle.label | target [taint8] | | tst.js:391:19:391:31 | target.taint8 | semmle.label | target.taint8 | | tst.js:392:18:392:23 | target [taint8] | semmle.label | target [taint8] | | tst.js:392:18:392:30 | target.taint8 | semmle.label | target.taint8 | diff --git a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected index f8781e6741b9..c508da8aee2a 100644 --- a/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected +++ b/javascript/ql/test/query-tests/Security/CWE-079/DomBasedXss/XssWithAdditionalSources.expected @@ -658,7 +658,6 @@ nodes | tst.js:389:18:389:30 | target.taint7 | semmle.label | target.taint7 | | tst.js:391:3:391:8 | [post update] target [taint8] | semmle.label | [post update] target [taint8] | | tst.js:391:19:391:24 | target | semmle.label | target | -| tst.js:391:19:391:24 | target [taint8] | semmle.label | target [taint8] | | tst.js:391:19:391:31 | target.taint8 | semmle.label | target.taint8 | | tst.js:392:18:392:23 | target [taint8] | semmle.label | target [taint8] | | tst.js:392:18:392:30 | target.taint8 | semmle.label | target.taint8 | @@ -1302,10 +1301,8 @@ edges | tst.js:375:18:375:23 | target [taint3] | tst.js:375:18:375:30 | target.taint3 | provenance | | | tst.js:380:18:380:23 | target | tst.js:380:18:380:30 | target.taint5 | provenance | | | tst.js:389:18:389:23 | target | tst.js:389:18:389:30 | target.taint7 | provenance | | -| tst.js:391:3:391:8 | [post update] target [taint8] | tst.js:391:19:391:24 | target [taint8] | provenance | | | tst.js:391:3:391:8 | [post update] target [taint8] | tst.js:392:18:392:23 | target [taint8] | provenance | | | tst.js:391:19:391:24 | target | tst.js:391:19:391:31 | target.taint8 | provenance | | -| tst.js:391:19:391:24 | target [taint8] | tst.js:391:19:391:31 | target.taint8 | provenance | | | tst.js:391:19:391:31 | target.taint8 | tst.js:391:3:391:8 | [post update] target [taint8] | provenance | | | tst.js:392:18:392:23 | target [taint8] | tst.js:392:18:392:30 | target.taint8 | provenance | | | tst.js:399:7:399:13 | payload | tst.js:400:18:400:24 | payload | provenance | | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected index 17d280d38096..eac2633114f5 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected @@ -45,7 +45,6 @@ | json-schema-validator.js:55:22:55:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:55:22:55:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value | | json-schema-validator.js:59:22:59:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:59:22:59:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value | | json-schema-validator.js:61:22:61:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:61:22:61:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value | -| koarouter.js:17:27:17:77 | `SELECT ... nd ')}` | koarouter.js:5:13:5:19 | version | koarouter.js:17:27:17:77 | `SELECT ... nd ')}` | This query string depends on a $@. | koarouter.js:5:13:5:19 | version | user-provided value | | ldap.js:28:30:28:34 | opts1 | ldap.js:20:21:20:27 | req.url | ldap.js:28:30:28:34 | opts1 | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value | | ldap.js:32:5:32:61 | { filte ... e}))` } | ldap.js:20:21:20:27 | req.url | ldap.js:32:5:32:61 | { filte ... e}))` } | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value | | ldap.js:66:30:66:53 | { filte ... ilter } | ldap.js:20:21:20:27 | req.url | ldap.js:66:30:66:53 | { filte ... ilter } | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value | @@ -302,13 +301,6 @@ edges | json-schema-validator.js:50:15:50:19 | query | json-schema-validator.js:61:22:61:26 | query | provenance | | | json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) | json-schema-validator.js:50:15:50:19 | query | provenance | | | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:50:23:50:48 | JSON.pa ... y.data) | provenance | Config | -| koarouter.js:5:13:5:19 | version | koarouter.js:5:13:5:19 | version | provenance | | -| koarouter.js:5:13:5:19 | version | koarouter.js:14:38:14:44 | version | provenance | | -| koarouter.js:14:9:14:18 | [post update] conditions [ArrayElement] | koarouter.js:17:52:17:61 | conditions [ArrayElement] | provenance | | -| koarouter.js:14:25:14:46 | `versio ... rsion}` | koarouter.js:14:9:14:18 | [post update] conditions [ArrayElement] | provenance | | -| koarouter.js:14:38:14:44 | version | koarouter.js:14:25:14:46 | `versio ... rsion}` | provenance | | -| koarouter.js:17:52:17:61 | conditions [ArrayElement] | koarouter.js:17:52:17:75 | conditi ... and ') | provenance | | -| koarouter.js:17:52:17:75 | conditi ... and ') | koarouter.js:17:27:17:77 | `SELECT ... nd ')}` | provenance | | | ldap.js:20:7:20:7 | q | ldap.js:22:18:22:18 | q | provenance | | | ldap.js:20:11:20:34 | url.par ... , true) | ldap.js:20:7:20:7 | q | provenance | | | ldap.js:20:21:20:27 | req.url | ldap.js:20:11:20:34 | url.par ... , true) | provenance | | @@ -790,14 +782,6 @@ nodes | json-schema-validator.js:55:22:55:26 | query | semmle.label | query | | json-schema-validator.js:59:22:59:26 | query | semmle.label | query | | json-schema-validator.js:61:22:61:26 | query | semmle.label | query | -| koarouter.js:5:13:5:19 | version | semmle.label | version | -| koarouter.js:5:13:5:19 | version | semmle.label | version | -| koarouter.js:14:9:14:18 | [post update] conditions [ArrayElement] | semmle.label | [post update] conditions [ArrayElement] | -| koarouter.js:14:25:14:46 | `versio ... rsion}` | semmle.label | `versio ... rsion}` | -| koarouter.js:14:38:14:44 | version | semmle.label | version | -| koarouter.js:17:27:17:77 | `SELECT ... nd ')}` | semmle.label | `SELECT ... nd ')}` | -| koarouter.js:17:52:17:61 | conditions [ArrayElement] | semmle.label | conditions [ArrayElement] | -| koarouter.js:17:52:17:75 | conditi ... and ') | semmle.label | conditi ... and ') | | ldap.js:20:7:20:7 | q | semmle.label | q | | ldap.js:20:11:20:34 | url.par ... , true) | semmle.label | url.par ... , true) | | ldap.js:20:21:20:27 | req.url | semmle.label | req.url | diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/koarouter.js b/javascript/ql/test/query-tests/Security/CWE-089/untyped/koarouter.js index 3f7254c9a264..e07fa929063c 100644 --- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/koarouter.js +++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/koarouter.js @@ -2,7 +2,7 @@ const Router = require('koa-router') const {Sequelize} = require("sequelize"); new Router().get("/hello", (ctx) => { - const { version } = ctx.query; // $ Source + const { version } = ctx.query; if (version && validVersion(version) === false) { throw new Error(`invalid version ${version}`); @@ -14,7 +14,7 @@ new Router().get("/hello", (ctx) => { conditions.push(`version = ${version}`) } - new Sequelize().query(`SELECT * FROM t WHERE ${conditions.join(' and ')}`, null); // $ Alert + new Sequelize().query(`SELECT * FROM t WHERE ${conditions.join(' and ')}`, null); }); function validVersion(version) { diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected index 7eb7c2fdf90a..eab4dd0ead0e 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/PrototypePollutingFunction.expected @@ -17,7 +17,6 @@ | tests.js:280:13:280:15 | dst | tests.js:276:34:276:36 | key | tests.js:280:13:280:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:276:21:276:23 | src | src | tests.js:280:13:280:15 | dst | dst | | tests.js:308:17:308:19 | dst | tests.js:302:14:302:16 | key | tests.js:308:17:308:19 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:302:21:302:23 | src | src | tests.js:308:17:308:19 | dst | dst | | tests.js:322:17:322:19 | dst | tests.js:315:14:315:16 | key | tests.js:322:17:322:19 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:315:21:315:23 | src | src | tests.js:322:17:322:19 | dst | dst | -| tests.js:338:17:338:19 | dst | tests.js:329:14:329:16 | key | tests.js:338:17:338:19 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:329:21:329:23 | src | src | tests.js:338:17:338:19 | dst | dst | | tests.js:357:17:357:22 | target | tests.js:350:37:350:39 | key | tests.js:357:17:357:22 | target | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:350:21:350:26 | source | source | tests.js:357:17:357:22 | target | target | | tests.js:403:13:403:15 | dst | tests.js:381:14:381:16 | key | tests.js:403:13:403:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:381:21:381:23 | obj | obj | tests.js:403:13:403:15 | dst | dst | | tests.js:419:13:419:15 | dst | tests.js:413:14:413:16 | key | tests.js:419:13:419:15 | dst | Properties are copied from $@ to $@ without guarding against prototype pollution. | tests.js:413:21:413:23 | src | src | tests.js:419:13:419:15 | dst | dst | @@ -396,22 +395,11 @@ edges | tests.js:320:38:320:45 | dst[key] | tests.js:314:31:314:33 | dst | provenance | | | tests.js:320:42:320:44 | key | tests.js:320:38:320:45 | dst[key] | provenance | Config | | tests.js:320:48:320:52 | value | tests.js:314:36:314:38 | src | provenance | | -| tests.js:328:25:328:27 | dst | tests.js:336:32:336:34 | dst | provenance | | -| tests.js:328:25:328:27 | dst | tests.js:338:17:338:19 | dst | provenance | | | tests.js:328:30:328:32 | src | tests.js:336:42:336:44 | src | provenance | | | tests.js:328:30:328:32 | src | tests.js:338:28:338:30 | src | provenance | | -| tests.js:329:14:329:16 | key | tests.js:336:36:336:38 | key | provenance | | -| tests.js:329:14:329:16 | key | tests.js:336:46:336:48 | key | provenance | | -| tests.js:329:14:329:16 | key | tests.js:338:21:338:23 | key | provenance | | -| tests.js:329:14:329:16 | key | tests.js:338:32:338:34 | key | provenance | | -| tests.js:336:32:336:34 | dst | tests.js:336:32:336:39 | dst[key] | provenance | Config | -| tests.js:336:32:336:39 | dst[key] | tests.js:328:25:328:27 | dst | provenance | | -| tests.js:336:36:336:38 | key | tests.js:336:32:336:39 | dst[key] | provenance | Config | | tests.js:336:42:336:44 | src | tests.js:336:42:336:49 | src[key] | provenance | Config | | tests.js:336:42:336:49 | src[key] | tests.js:328:30:328:32 | src | provenance | | -| tests.js:336:46:336:48 | key | tests.js:336:42:336:49 | src[key] | provenance | Config | | tests.js:338:28:338:30 | src | tests.js:338:28:338:35 | src[key] | provenance | Config | -| tests.js:338:32:338:34 | key | tests.js:338:28:338:35 | src[key] | provenance | Config | | tests.js:348:32:348:37 | target | tests.js:349:26:349:31 | target | provenance | | | tests.js:348:32:348:37 | target | tests.js:361:12:361:17 | target | provenance | | | tests.js:348:40:348:45 | source | tests.js:349:54:349:59 | source | provenance | | @@ -1092,20 +1080,11 @@ nodes | tests.js:322:17:322:19 | dst | semmle.label | dst | | tests.js:322:21:322:23 | key | semmle.label | key | | tests.js:322:28:322:32 | value | semmle.label | value | -| tests.js:328:25:328:27 | dst | semmle.label | dst | | tests.js:328:30:328:32 | src | semmle.label | src | -| tests.js:329:14:329:16 | key | semmle.label | key | -| tests.js:336:32:336:34 | dst | semmle.label | dst | -| tests.js:336:32:336:39 | dst[key] | semmle.label | dst[key] | -| tests.js:336:36:336:38 | key | semmle.label | key | | tests.js:336:42:336:44 | src | semmle.label | src | | tests.js:336:42:336:49 | src[key] | semmle.label | src[key] | -| tests.js:336:46:336:48 | key | semmle.label | key | -| tests.js:338:17:338:19 | dst | semmle.label | dst | -| tests.js:338:21:338:23 | key | semmle.label | key | | tests.js:338:28:338:30 | src | semmle.label | src | | tests.js:338:28:338:35 | src[key] | semmle.label | src[key] | -| tests.js:338:32:338:34 | key | semmle.label | key | | tests.js:348:32:348:37 | target | semmle.label | target | | tests.js:348:40:348:45 | source | semmle.label | source | | tests.js:349:26:349:31 | target | semmle.label | target | diff --git a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js index dcce195526ac..59c22a974049 100644 --- a/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js +++ b/javascript/ql/test/query-tests/Security/CWE-915/PrototypePollutingFunction/tests.js @@ -326,7 +326,7 @@ function mergeRephinementNode(dst, src) { } function mergeSelective(dst, src) { - for (let key in src) { // $ Source + for (let key in src) { if (src.hasOwnProperty(key)) { // Only 'prefs' is merged recursively if (key in dst && key !== 'prefs') { @@ -335,7 +335,7 @@ function mergeSelective(dst, src) { if (dst[key]) { mergeSelective(dst[key], src[key]); } else { - dst[key] = src[key]; // $ Alert + dst[key] = src[key]; } } } diff --git a/shared/ssa/codeql/ssa/Ssa.qll b/shared/ssa/codeql/ssa/Ssa.qll index 6cc685f8ef8b..a7fcb3f6c075 100644 --- a/shared/ssa/codeql/ssa/Ssa.qll +++ b/shared/ssa/codeql/ssa/Ssa.qll @@ -1483,6 +1483,15 @@ module Make< */ default predicate includeWriteDefsInFlowStep() { any() } + /** + * Holds if phi definitions should always have a data flow node, even when + * they have a unique next reference. This is useful for language-specific + * APIs that expose a node for each SSA definition. + * + * Phi reads may still be skipped. + */ + default predicate includePhiDefsInFlowStep() { none() } + /** * Holds if barrier guards should be supported on input edges to phi * nodes. Disable this only if barrier guards are not going to be used. @@ -1540,9 +1549,20 @@ module Make< ) } + private Definition getAValuePreservingInputDef(Definition def) { + result = def + or + exists(Definition inp | + DfInput::allowFlowIntoUncertainDef(def) and + not DfInput::ssaDefHasSource(def) and + uncertainWriteDefinitionInput(def, inp) and + result = getAValuePreservingInputDef(inp) + ) + } + pragma[nomagic] private predicate phiInputHasRead(SsaPhiExt phi, BasicBlock input) { - exists(DfInput::getARead(getAPhiInputDef(phi, input))) + exists(DfInput::getARead(getAValuePreservingInputDef(getAPhiInputDef(phi, input)))) } /** Holds if `bb` is the target end of a branch edge of a guard and the guard controls `bb`. */ @@ -1667,6 +1687,7 @@ module Make< * flow edges. */ private predicate phiHasUniqNextNode(SsaPhiExt phi) { + not (phi instanceof PhiNode and DfInput::includePhiDefsInFlowStep()) and not relevantBackEdge(phi, _) and exists(int nextPhiInput, int nextPhi, int nextRef | 1 = nextPhiInput + nextPhi + nextRef and @@ -2119,7 +2140,7 @@ module Make< private predicate guardChecksSsaDef( DfInput::Guard g, Definition def, DfInput::GuardValue val, State state ) { - guardChecks(g, DfInput::getARead(def), val, state) + guardChecks(g, DfInput::getARead(getAValuePreservingInputDef(def)), val, state) } private module Barrier = BarrierGuardDefWithState;