Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -406,6 +411,7 @@ module NameResolution {
/** Helps track flow from a particular set of source nodes. */
module Track<nodeSig/1 isSource> {
/** Gets the set of nodes reachable from `source`. */
pragma[nomagic]
Node track(Node source) {
isSource(source) and
result = source
Expand All @@ -419,6 +425,7 @@ module NameResolution {
/** Helps track flow from a particular set of source nodes. */
module TrackNode<AstNodeSig Source> {
/** Gets the set of nodes reachable from `source`. */
pragma[nomagic]
Node track(Source source) {
result = source
or
Expand Down Expand Up @@ -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
Expand Down
43 changes: 39 additions & 4 deletions javascript/ql/lib/semmle/javascript/internal/TypeResolution.qll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -198,6 +221,7 @@ module TypeResolution {
)
}

pragma[nomagic]
predicate contextualType(Node value, Node type) {
exists(LocalVariableLike v |
type = v.getADeclaration().getTypeAnnotation() and
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
)
}

Expand All @@ -309,6 +341,7 @@ module TypeResolution {
* - a union type has the property if all its members have the property
*/
module TrackMustProp<nodeSig/1 directlyHasProperty> {
pragma[nomagic]
predicate hasProperty(Node node) {
directlyHasProperty(node)
or
Expand Down Expand Up @@ -341,6 +374,7 @@ module TypeResolution {
}

module ValueHasProperty<nodeSig/1 typeHasProperty> {
pragma[nomagic]
predicate valueHasProperty(Node value) {
exists(Node type |
valueHasType(value, type) and
Expand Down Expand Up @@ -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
Expand Down