diff --git a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll index b25c98fd693b..43ae4ffd43e6 100644 --- a/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/NameResolution.qll @@ -73,6 +73,7 @@ module NameResolution { * * May also include some type-specific steps in cases where this is harmless when tracking values. */ + pragma[nomagic] private predicate commonStep(Node node1, Node node2) { // Import paths are part of the graph and has an incoming edge from the imported module, if found. // This ensures we can also use the PathExpr as a source when working with external (unresolved) modules. @@ -187,6 +188,7 @@ module NameResolution { /** * Holds if there is a read from `node1` to `node2` that accesses the member `name`. */ + pragma[nomagic] predicate readStep(Node node1, string name, Node node2) { exists(QualifiedTypeAccess access | node1 = access.getQualifier() and @@ -321,6 +323,7 @@ module NameResolution { /** * Gets the exported member of `mod` named `name`. */ + pragma[nomagic] Node getModuleExport(ModuleLike mod, string name) { exists(ExportDeclaration exprt | mod = exprt.getContainer() and @@ -362,6 +365,7 @@ module NameResolution { * Holds if `value` is stored in `target.prop`. Only needs to recognise assignments * that are also recognised by JSDoc tooling such as the Closure compiler. */ + pragma[nomagic] private predicate storeToVariable(Expr value, string prop, LocalVariableLike target) { exists(AssignExpr assign | // target.name = value @@ -374,6 +378,7 @@ module NameResolution { } /** Steps that only apply for this configuration. */ + pragma[nomagic] private predicate specificStep(Node node1, Node node2) { exists(LexicalName var | S::isRelevantVariable(var) | node1.(LexicalDecl).getALexicalName() = var and @@ -406,6 +411,7 @@ module NameResolution { /** Helps track flow from a particular set of source nodes. */ module Track { /** Gets the set of nodes reachable from `source`. */ + pragma[nomagic] Node track(Node source) { isSource(source) and result = source @@ -419,6 +425,7 @@ module NameResolution { /** Helps track flow from a particular set of source nodes. */ module TrackNode { /** Gets the set of nodes reachable from `source`. */ + pragma[nomagic] Node track(Source source) { result = source or @@ -482,6 +489,7 @@ module NameResolution { * * Unlike `trackModule`, this is intended to track uses of external packages. */ + pragma[nomagic] predicate nodeRefersToModule(Node node, string mod, string qualifiedName) { exists(Expr path | path = any(Import imprt).getImportedPathExpr() or diff --git a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll index a158ed6421a5..73885f833051 100644 --- a/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll +++ b/javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll @@ -12,6 +12,7 @@ module TypeResolution { * We track through underlying types as an approximate way to handle calls to a type * that is a union/intersection involving functions. */ + pragma[nomagic] Node trackUnderlyingFunctionType(Function fun) { result = fun or @@ -139,6 +140,28 @@ module TypeResolution { ) } + /** + * `ContentSet.getAReadContent` restricted to the content sets and contents relevant for type resolution. + */ + pragma[nomagic] + private DataFlow::Content getAReadContentRestricted(DataFlow::ContentSet cs) { + valueReadStep(_, cs, _) and + result = cs.getAReadContent() and + typeMember(_, result, _) + } + + /** + * `valueReadStep` where the `ContentSet` has been mapped to the set of relevant read-contents. + */ + pragma[nomagic] + private predicate valueReadStepOnContent(Node object, DataFlow::Content content, Node member) { + exists(DataFlow::ContentSet contents | + valueReadStep(object, contents, member) and + content = getAReadContentRestricted(contents) + ) + } + + pragma[nomagic] predicate callTarget(InvokeExpr call, Function target) { exists(ClassDefinition cls | valueHasType(call.(NewExpr).getCallee(), trackClassValue(cls)) and @@ -198,6 +221,7 @@ module TypeResolution { ) } + pragma[nomagic] predicate contextualType(Node value, Node type) { exists(LocalVariableLike v | type = v.getADeclaration().getTypeAnnotation() and @@ -239,6 +263,7 @@ module TypeResolution { /** * Holds if `value` has the given `type`. */ + cached predicate valueHasType(Node value, Node type) { value.(BindingPattern).getTypeAnnotation() = type or @@ -293,11 +318,18 @@ module TypeResolution { or exists(Node mid | valueHasType(mid, type) | ValueFlow::step(mid, value)) or - exists(Node mid, Node midType, DataFlow::ContentSet contents, Node host | - valueReadStep(mid, contents, value) and + exists(DataFlow::Content content, Node host | + typeMemberHostRead(host, content, value) and + typeMember(host, content, type) + ) + } + + pragma[nomagic] + private predicate typeMemberHostRead(Node host, DataFlow::Content content, Node target) { + exists(Node mid, Node midType | + valueReadStepOnContent(mid, content, target) and valueHasType(mid, midType) and - typeMemberHostReaches(host, midType) and - typeMember(host, contents.getAReadContent(), type) + typeMemberHostReaches(host, midType) ) } @@ -309,6 +341,7 @@ module TypeResolution { * - a union type has the property if all its members have the property */ module TrackMustProp { + pragma[nomagic] predicate hasProperty(Node node) { directlyHasProperty(node) or @@ -341,6 +374,7 @@ module TypeResolution { } module ValueHasProperty { + pragma[nomagic] predicate valueHasProperty(Node value) { exists(Node type | valueHasType(value, type) and @@ -405,6 +439,7 @@ module TypeResolution { /** * Holds if `type` contains `string` or `any`, possibly wrapped in a promise. */ + pragma[nomagic] predicate hasUnderlyingStringOrAnyType(Node type) { type.(TypeAnnotation).isStringy() or