diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll index 38d339a85278..8b4acbee61eb 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/experimental/adaptivethreatmodeling/StandardEndpointFilters.qll @@ -53,7 +53,11 @@ predicate isSomeModeledArgument(DataFlow::Node n) { /** * Holds if `n` appears to be a numeric value. */ -predicate isNumeric(DataFlow::Node n) { isReadFrom(n, ".*index.*") } +// Performance optimisation: This predicate operates on a large set of +// starting nodes, so use binding hints to suggest computing that set last. +predicate isNumeric(DataFlow::Node n) { + getAnAccessedName(pragma[only_bind_into](n)).regexpMatch(".*index.*") +} /** * Holds if `n` is an argument to a library without sinks. diff --git a/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll b/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll index 924e8f0a1664..34c34c7f46e0 100644 --- a/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll +++ b/javascript/ql/lib/semmle/javascript/GlobalAccessPaths.qll @@ -462,15 +462,15 @@ module AccessPath { ReachableBasicBlock bb, Root root, string path, int ranking, AccessPathKind type ) { result = - rank[ranking](ControlFlowNode ref | + rank[ranking](ControlFlowNode ref, int i | ref = getAccessTo(root, path, _) and - ref.getBasicBlock() = bb and + ref = bb.getNode(i) and // Prunes the accesses where there does not exists a read and write within the same basicblock. // This could be more precise, but doing it like this avoids massive joins. hasRead(bb) and hasWrite(bb) | - ref order by any(int i | ref = bb.getNode(i)) + ref order by i ) and result = getAccessTo(root, path, type) } @@ -492,7 +492,7 @@ module AccessPath { */ pragma[noinline] private predicate hasWrite(ReachableBasicBlock bb) { - bb = getAccessTo(_, _, AccessPathRead()).getBasicBlock() + bb = getAccessTo(_, _, AccessPathWrite()).getBasicBlock() } /** @@ -565,9 +565,12 @@ module AccessPath { ) or // across basic blocks. - exists(Root root, string path | + exists(Root root, string path, ReachableBasicBlock readBlock | read.asExpr() = getAccessTo(root, path, AccessPathRead()) and - getAWriteBlock(root, path).strictlyDominates(read.getBasicBlock()) + readBlock = read.getBasicBlock() and + // Performance optimisation: check that `read` is in a *reachable* basic block + // before looking for a dominating write block. + getAWriteBlock(root, path).strictlyDominates(pragma[only_bind_out](readBlock)) ) } } diff --git a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableTypeInference.qll b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableTypeInference.qll index 8155ad813190..6f611da31532 100644 --- a/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableTypeInference.qll +++ b/javascript/ql/lib/semmle/javascript/dataflow/internal/VariableTypeInference.qll @@ -217,7 +217,7 @@ private class AnalyzedImplicitInit extends AnalyzedSsaDefinition, SsaImplicitIni */ private class AnalyzedVariableCapture extends AnalyzedSsaDefinition, SsaVariableCapture { override AbstractValue getAnRhsValue() { - exists(LocalVariable v | v = getSourceVariable() | + exists(LocalVariable v | v = this.getSourceVariable() | result = v.(AnalyzedCapturedVariable).getALocalValue() or result = any(AnalyzedExplicitDefinition def | def.getSourceVariable() = v).getAnRhsValue() diff --git a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll index aa3aa81bffe8..45a132e088a2 100644 --- a/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll +++ b/javascript/ql/lib/semmle/javascript/filters/ClassifyFiles.qll @@ -56,9 +56,7 @@ predicate isGeneratedCodeFile(File f) { isGenerated(f.getATopLevel()) } predicate isTestFile(File f) { exists(Test t | t.getFile() = f) or - exists(string stemExt | stemExt = "test" or stemExt = "spec" | - f = getTestFile(any(File orig), stemExt) - ) + f = getATestFile(_) or f.getAbsolutePath().regexpMatch(".*/__(mocks|tests)__/.*") } diff --git a/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll b/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll index fb2d85523d4f..262d90e84994 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/Testing.qll @@ -40,7 +40,7 @@ class BDDTest extends Test, @call_expr { /** * Gets the test file for `f` with stem extension `stemExt`. - * That is, a file named file named `..` in the + * That is, a file named `..` in the * same directory as `f` which is named `.`. */ bindingset[stemExt] @@ -48,6 +48,33 @@ File getTestFile(File f, string stemExt) { result = f.getParentContainer().getFile(f.getStem() + "." + stemExt + "." + f.getExtension()) } +/** + * Gets a test file for `f`. + * That is, a file named `..` in the + * same directory as `f`, where `f` is named `.` and + * `` is a well-known test file identifier, such as `test` or `spec`. + */ +File getATestFile(File f) { + result = f.getParentContainer().getFile(getATestFileName(f)) +} + +/** + * Gets a name of a test file for `f`. + * That is, `..` where + * `f` is named `.` and `` is + * a well-known test file identifier, such as `test` or `spec`. + */ +// Helper predicate factored out for performance. +// This predicate is linear in the size of f, and forces +// callers to join only once against f rather than two separate joins +// when computing the stem and the extension. +// This loses some flexibility because callers cannot specify +// an arbitrary stemExt. +pragma[nomagic] +private string getATestFileName(File f) { + result = f.getStem() + "." + ["test", "spec"] + "." + f.getExtension() +} + /** * A Jest test, that is, an invocation of a global function named * `test` where the first argument is a string and the second diff --git a/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll b/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll index 12356d1bf42b..de7ca2a852e5 100644 --- a/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll +++ b/javascript/ql/lib/semmle/javascript/heuristics/SyntacticHeuristics.qll @@ -16,15 +16,23 @@ import javascript */ bindingset[regexp] predicate isReadFrom(DataFlow::Node read, string regexp) { + getAnAccessedName(read).regexpMatch(regexp) +} + +/** + * Gets the "name" accessed by `read`. The "name" is one of: + * - the name of the read variable, if `read` is a variable read + * - the name of the read property, if `read` is a property read + * - the suffix of the getter-method name, if `read` is a getter invocation, for example "Number" in "getNumber" + */ +string getAnAccessedName(DataFlow::Node read) { exists(DataFlow::Node actualRead | actualRead = read.asExpr().getUnderlyingValue().(LogOrExpr).getAnOperand().flow() or // unfold `x || y` once actualRead = read | - exists(string name | name.regexpMatch(regexp) | - actualRead.asExpr().getUnderlyingValue().(VarAccess).getName() = name or - actualRead.(DataFlow::PropRead).getPropertyName() = name or - actualRead.(DataFlow::InvokeNode).getCalleeName() = "get" + name - ) + actualRead.asExpr().getUnderlyingValue().(VarAccess).getName() = result or + actualRead.(DataFlow::PropRead).getPropertyName() = result or + actualRead.(DataFlow::InvokeNode).getCalleeName() = "get" + result ) } diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll index 867494fc0a36..37b1830018e7 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/CorsMisconfigurationForCredentialsCustomizations.qll @@ -50,8 +50,12 @@ module CorsMisconfigurationForCredentials { | routeHandler.getAResponseHeader(_) = origin and routeHandler.getAResponseHeader(_) = credentials and - origin.definesExplicitly("access-control-allow-origin", this.asExpr()) and - credentials.definesExplicitly("access-control-allow-credentials", credentialsValue) + // Performance optimisation: start with the set of all route handlers + // rather than the set of all exprs. + pragma[only_bind_into](origin) + .definesExplicitly("access-control-allow-origin", this.asExpr()) and + pragma[only_bind_into](credentials) + .definesExplicitly("access-control-allow-credentials", credentialsValue) | credentialsValue.mayHaveBooleanValue(true) or credentialsValue.mayHaveStringValue("true") diff --git a/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll b/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll index 501eed347c55..2f27bb69b9bf 100644 --- a/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll +++ b/javascript/ql/lib/semmle/javascript/security/dataflow/Xss.qll @@ -437,8 +437,27 @@ module DomBasedXss { b = phi.getAnInput().getDefinition() and count(phi.getAnInput()) = 2 and not a = b and - sanitizer = DataFlow::valueNode(a.getDef().getSource()) and - sanitizer.getAnArgument().asExpr().(VarAccess).getVariable() = b.getSourceVariable() + /* + * Performance optimisation: + * + * When join-ordering and evaluating this conjunction, + * it is preferable to start with the relatively small set of + * `sanitizer` calls, then compute the set of SSA variables accessed + * as the arguments of those sanitizer calls, then reason about how + * those variables are used in phi nodes. + * + * Use directional binding pragmas to encourage this join order, + * starting with `sanitizer`. + * + * Without these pragmas, the join orderer may choose the opposite order: + * start with all `phi` nodes, then compute the set of SSA variables involved, + * then the (potentially large) set of accesses to those variables, + * then the set of accesses used as the argument of a sanitizer call. + */ + + pragma[only_bind_out](sanitizer) = DataFlow::valueNode(a.getDef().getSource()) and + pragma[only_bind_out](sanitizer.getAnArgument().asExpr()) = + b.getSourceVariable().getAnAccess() | pred = DataFlow::ssaDefinitionNode(b) and succ = DataFlow::ssaDefinitionNode(phi) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy