CodeQL library for JavaScript/TypeScript
codeql/javascript-all 0.8.12 (changelog, source)
Search

Class API::Node

A node in the API graph, representing a value that has crossed the boundary between this codebase and an external library (or in general, any external codebase).

Basic usage

API graphs are typically used to identify “API calls”, that is, calls to an external function whose implementation is not necessarily part of the current codebase.

The most basic use of API graphs is typically as follows:

  1. Start with API::moduleImport for the relevant library.
  2. Follow up with a chain of accessors such as getMember describing how to get to the relevant API function.
  3. Map the resulting API graph nodes to data-flow nodes, using asSource or asSink.

For example, a simplified way to get arguments to underscore.extend would be

API::moduleImport("underscore").getMember("extend").getParameter(0).asSink()

The most commonly used accessors are getMember, getParameter, and getReturn.

API graph nodes

There are two kinds of nodes in the API graphs, distinguished by who is “holding” the value:

  • Use-nodes represent values held by the current codebase, which came from an external library. (The current codebase is “using” a value that came from the library).
  • Def-nodes represent values held by the external library, which came from this codebase. (The current codebase “defines” the value seen by the library).

API graph nodes are associated with data-flow nodes in the current codebase. (Since external libraries are not part of the database, there is no way to associate with concrete data-flow nodes from the external library).

  • Use-nodes are associated with data-flow nodes where a value enters the current codebase, such as the return value of a call to an external function.
  • Def-nodes are associated with data-flow nodes where a value leaves the current codebase, such as an argument passed in a call to an external function.

Access paths and edge labels

Nodes in the API graph are associated with a set of access paths, describing a series of operations that may be performed to obtain that value.

For example, the access path API::moduleImport("lodash").getMember("extend") represents the action of importing lodash and then accessing the member extend on the resulting object. It would be associated with an expression such as require("lodash").extend.

Each edge in the graph is labelled by such an “operation”. For an edge A->B, the type of the A node determines who is performing the operation, and the type of the B node determines who ends up holding the result:

  • An edge starting from a use-node describes what the current codebase is doing to a value that came from a library.
  • An edge starting from a def-node describes what the external library might do to a value that came from the current codebase.
  • An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node).
  • An edge ending in a def-node means the result ends up in external code (its associated data-flow node is the place where it was “last seen” in the current codebase before flowing out)

Because the implementation of the external library is not visible, it is not known exactly what operations it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform those operations. (When constructing these edges, we assume the library is somewhat well-behaved).

For example, given this snippet:

require('foo')(x => { doSomething(x) })

A callback is passed to the external function foo. We can’t know if foo will actually invoke this callback. But if the library should decide to invoke the callback, then a value will flow into the current codebase via the x parameter. For that reason, an edge is generated representing the argument-passing operation that might be performed by foo. This edge is going from the def-node associated with the callback to the use-node associated with the parameter x.

Thinking in operations versus code patterns

Treating edges as “operations” helps avoid a pitfall in which library models become overly specific to certain code patterns. Consider the following two equivalent calls to foo:

const foo = require('foo');

foo({
  myMethod(x) {...}
});

foo({
  get myMethod() {
    return function(x) {...}
  }
});

If foo calls myMethod on its first parameter, either of the myMethod implementations will be invoked. And indeed, the access path API::moduleImport("foo").getParameter(0).getMember("myMethod").getParameter(0) correctly identifies both x parameters.

Observe how getMember("myMethod") behaves when the member is defined via a getter. When thinking in code patterns, it might seem obvious that getMember should have obtained a reference to the getter method itself. But when seeing it as an access to myMethod performed by the library, we can deduce that the relevant expression on the client side is actually the return-value of the getter.

Although one may think of API graphs as a tool to find certain program elements in the codebase, it can lead to some situations where intuition does not match what works best in practice.

Import path

import javascript

Direct supertypes

Known direct subtypes

Predicates

asSink

Get a data-flow node where this value leaves the current codebase and flows into an external library (or in general, any external codebase).

asSource

Get a data-flow node where this value enters the current codebase.

getACall

Gets a call to the function represented by this API component.

getADecoratedClass

Gets any class that has this value as a decorator.

getADecoratedMember

Gets any method, field, or accessor that has this value as a decorator.

getADecoratedParameter

Gets any parameter that has this value as a decorator.

getAMember

Gets a node representing a member of this API component where the name of the member may or may not be known statically.

getAParameter

Gets a node representing a parameter of the function represented by this node.

getAPredecessor

Gets a node such that there is an edge in the API graph between this node and the other one.

getAPredecessor

Gets a node such that there is an edge in the API graph between that other node and this one, and that edge is labeled with lbl

getASuccessor

Gets a node such that there is an edge in the API graph between that other node and this one.

getASuccessor

Gets a node such that there is an edge in the API graph between this node and the other one, and that edge is labeled with lbl.

getAValueReachableFromSource

Get a data-flow node where this value may flow after entering the current codebase.

getAValueReachingSink

Get a data-flow node that transitively flows to an external library (or in general, any external codebase).

getAnInstantiation

Gets a new call to the function represented by this API component.

getAnInvocation

Gets an invocation (with our without new) to the function represented by this API component.

getDepth

Gets the shortest distance from the root to this node in the API graph.

getForwardingFunction

Gets a node representing a function that is a wrapper around the function represented by this node.

getInducingNode

Gets the data-flow node that gives rise to this node, if any.

getInstance

Gets a node representing an instance of this API component, that is, an object whose constructor is the function represented by this node.

getLastParameter

Gets a node representing the last parameter of the function represented by this node.

getMaybePromisifiedCall

Gets a call to the function represented by this API component, or a promisified version of the function.

getMember

Gets a node representing member m of this API component.

getNumParameter

Gets the number of parameters of the function represented by this node.

getParameter

Gets a node representing the ith parameter of the function represented by this node.

getPath

Gets a string representation of the lexicographically least among all shortest access paths from the root to this node.

getPromised

Gets a node representing the promised value wrapped in the Promise object represented by this node.

getPromisedError

Gets a node representing the error wrapped in the Promise object represented by this node.

getReceiver

Gets a node representing the receiver of the function represented by this node.

getReturn

Gets a node representing the result of the function represented by this node.

getUnknownMember

Gets a node representing a member of this API component where the name of the member is not known statically.

hasLocationInfo

Holds if this node is located in file path between line startline, column startcol, and line endline, column endcol.

refersTo

Holds if this node may take its value from that node.

toString

Gets a textual representation of this node.