Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
8ea5fc7
JS: Stop dependeding on getPath() for toString()
asgerf Oct 10, 2025
8212bf4
JS: Make MkSyntheticCallbackArg() independent of trackUseNode
asgerf Oct 9, 2025
4ca39e4
JS: Make other node types not depend on tracking predicates
asgerf Oct 9, 2025
ce07c8a
JS: Make use() and rhs() uncached and private
asgerf Oct 9, 2025
9e1ab62
JS: Wrap in a module
asgerf Oct 9, 2025
a59e280
JS: Parameterise the module (still only one instantiation)
asgerf Oct 9, 2025
fed9be9
JS: Moving 'cache' annotations outside the parameterised module
asgerf Oct 9, 2025
8eb33cc
JS: Localize charpred of API::EntryPoint
asgerf Oct 10, 2025
38f1436
JS: Localize MkModuleExport
asgerf Oct 10, 2025
3c0d8bb
JS: Localize MkModuleImport
asgerf Oct 10, 2025
c0f27b2
JS: Localize MkClassInstance
asgerf Oct 10, 2025
17b0bcc
JS: Use forceLocal to localize MkTypeUse
asgerf Oct 10, 2025
329cb28
JS: Remove unused predicate
asgerf Oct 10, 2025
93333c0
JS: Make API nodes and labels local
asgerf Oct 10, 2025
6a15594
JS: Improve join order at MkUse call
asgerf Nov 28, 2025
85ee446
JS: Add missing def-node roots
asgerf Nov 28, 2025
9c7d636
JS: Call forceLocal on the output of Stage 1
asgerf Nov 28, 2025
00a4964
JS: Also expose "any state" version of tracking predicates
asgerf Nov 28, 2025
1e50800
JS: Restrict Stage1 to the base database
asgerf Nov 28, 2025
965f433
JS: Add overlay-specific Stage2
asgerf Nov 28, 2025
a54f881
JS:Add more member labels
asgerf Oct 10, 2025
79a751c
JS: Add debug tools for detecting lost nodes/edges
asgerf Oct 10, 2025
13fccbe
JS: Change signature of 'edges' to support quick eval
asgerf Oct 10, 2025
7b0ee6f
JS: Add back CallReceiverStep() restriction
asgerf Oct 6, 2025
eecfe15
JS: Remove global dependency that wasnt needed anyway
asgerf Nov 3, 2025
4cc3078
JS: Add overlay[global] to abstract classes with fields
asgerf Oct 10, 2025
11bba68
JS: Include import paths from custom ModuleImportNode::Range subclasses
asgerf Nov 20, 2025
98bf9ce
JS: Bugfix in Stage1Local::trackDefNode
asgerf Nov 24, 2025
f941358
JS: Simplify toString()
asgerf Nov 24, 2025
807c8b6
JS: Update test output to reflect Node.toString() change
asgerf Nov 24, 2025
1dfb5a1
JS: Work around an issue with overlay-invariance
asgerf Nov 24, 2025
47a6627
JS: Add back promisify-all support
asgerf Nov 24, 2025
9751a33
JS: Make API::Node overlay[local?]
asgerf Nov 24, 2025
f132dd5
JS: Sync ApiGraphModels.qll
asgerf Nov 24, 2025
f751754
JS: Fix some QL4QL alerts
asgerf Nov 25, 2025
5fbf877
JS: Add a missing needsDefNode restriction
asgerf Nov 25, 2025
6b14789
JS: Fix bad join in export logic
asgerf Dec 2, 2025
0a241e2
Update javascript/ql/lib/semmle/javascript/ApiGraphs.qll
asgerf Dec 9, 2025
4aa1368
Update javascript/ql/lib/semmle/javascript/ApiGraphs.qll
asgerf Dec 9, 2025
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
1,633 changes: 977 additions & 656 deletions javascript/ql/lib/semmle/javascript/ApiGraphs.qll

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/DOM.qll
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ module DOM {
* A data flow node or other program element that may refer to
* a DOM element.
*/
overlay[global]
abstract class Element extends Locatable {
ElementDefinition defn;

Expand Down
24 changes: 20 additions & 4 deletions javascript/ql/lib/semmle/javascript/ES2015Modules.qll
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class ES2015Module extends Module {
override string getName() { result = this.getFile().getStem() }

/** Gets an export declaration in this module. */
pragma[nomagic]
ExportDeclaration getAnExport() { result.getTopLevel() = this }

overlay[global]
Expand All @@ -38,6 +39,7 @@ class ES2015Module extends Module {

/** Holds if this module exports variable `v` under the name `name`. */
overlay[global]
pragma[nomagic]
predicate exportsAs(LexicalName v, string name) { this.getAnExport().exportsAs(v, name) }

override predicate isStrict() {
Expand Down Expand Up @@ -345,6 +347,7 @@ abstract class ExportDeclaration extends Stmt, @export_declaration {

/** Holds if this export declaration exports variable `v` under the name `name`. */
overlay[global]
pragma[nomagic]
final predicate exportsAs(LexicalName v, string name) {
this.exportsDirectlyAs(v, name)
or
Expand Down Expand Up @@ -821,18 +824,31 @@ class SelectiveReExportDeclaration extends ReExportDeclaration, ExportNamedDecla
result = ExportNamedDeclaration.super.getImportedPath()
}

overlay[global]
pragma[nomagic]
private predicate reExportsFrom(ES2015Module mod, string originalName, string reExportedName) {
exists(ExportSpecifier spec |
spec = this.getASpecifier() and
reExportedName = spec.getExportedName() and
originalName = spec.getLocalName() and
mod = this.getReExportedES2015Module()
)
}

overlay[global]
override predicate reExportsAs(LexicalName v, string name) {
exists(ExportSpecifier spec | spec = this.getASpecifier() and name = spec.getExportedName() |
this.getReExportedES2015Module().exportsAs(v, spec.getLocalName())
exists(ES2015Module mod, string originalName |
this.reExportsFrom(mod, originalName, name) and
mod.exportsAs(v, originalName)
) and
not (this.isTypeOnly() and v instanceof Variable)
}

overlay[global]
override DataFlow::Node getReExportedSourceNode(string name) {
exists(ExportSpecifier spec | spec = this.getASpecifier() and name = spec.getExportedName() |
result = this.getReExportedES2015Module().getAnExport().getSourceNode(spec.getLocalName())
exists(ES2015Module mod, string originalName |
this.reExportsFrom(mod, originalName, name) and
result = mod.getAnExport().getSourceNode(originalName)
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ module ClientRequest {
private string urlPropertyName() { result = "url" or result = "uri" }

/** An API entry-point for the global variable `axios`. */
overlay[local?]
private class AxiosGlobalEntryPoint extends API::EntryPoint {
AxiosGlobalEntryPoint() { this = "axiosGlobal" }

Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/D3.qll
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ private import semmle.javascript.security.dataflow.DomBasedXssCustomizations
/** Provides classes and predicates modeling aspects of the `d3` library. */
module D3 {
/** The global variable `d3` as an entry point for API graphs. */
overlay[local?]
private class D3GlobalEntry extends API::EntryPoint {
D3GlobalEntry() { this = "D3GlobalEntry" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module Electron {
BrowserView() { this = DataFlow::moduleMember("electron", "BrowserView").getAnInstantiation() }
}

overlay[local?]
private class ElectronEntryPoint extends API::EntryPoint {
ElectronEntryPoint() { this = "Electron.Browser" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ module EventRegistration {
/**
* A registration of an event handler on an EventEmitter.
*/
overlay[global]
abstract class Range extends DataFlow::Node {
EventEmitter::Range emitter;

Expand Down Expand Up @@ -148,6 +149,7 @@ module EventDispatch {
/**
* A dispatch of an event on an EventEmitter.
*/
overlay[global]
abstract class Range extends DataFlow::Node {
EventEmitter::Range emitter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import javascript
/** Provides classes modeling the [`history`](https://npmjs.org/package/history) library. */
module History {
/** The global variable `HistoryLibrary` as an entry point for API graphs. */
overlay[local?]
private class HistoryGlobalEntry extends API::EntryPoint {
HistoryGlobalEntry() { this = "HistoryLibrary" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ private module Immutable {
/**
* An API entrypoint for the global `Immutable` variable.
*/
overlay[local?]
private class ImmutableGlobalEntry extends API::EntryPoint {
ImmutableGlobalEntry() { this = "ImmutableGlobalEntry" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private module Console {
/**
* An API entrypoint for the global `console` variable.
*/
overlay[local?]
private class ConsoleGlobalEntry extends API::EntryPoint {
ConsoleGlobalEntry() { this = "ConsoleGlobalEntry" }

Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/Nest.qll
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ module NestJS {
}

/** API node entry point for custom implementations of `ValidationPipe` (a common pattern). */
overlay[local?]
private class ValidationNodeEntry extends API::EntryPoint {
ValidationNodeEntry() { this = "ValidationNodeEntry" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ module NodeJSLib {
DataFlow::Node getRouteHandlerNode() { result = handler }
}

overlay[global]
abstract private class HeaderDefinition extends Http::Servers::StandardHeaderDefinition {
ResponseNode r;

Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/Redux.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1099,6 +1099,7 @@ module Redux {
* Used to catch cases where the `connect` function was not recognized by API graphs (usually because of it being
* wrapped in another function, which API graphs won't look through).
*/
overlay[local?]
private class HeuristicConnectEntryPoint extends API::EntryPoint {
HeuristicConnectEntryPoint() { this = "react-redux-connect" }

Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/SQL.qll
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module SQL {
* An dataflow node that sanitizes a string to make it safe to embed into
* a SQL command.
*/
overlay[global]
abstract class SqlSanitizer extends DataFlow::Node {
DataFlow::Node input;
DataFlow::Node output;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ module Templating {
*/
bindingset[prefix]
string getDelimiterMatchingRegexpWithPrefix(string prefix) {
result = "(?s)" + prefix + "(" + concat("\\Q" + getADelimiter() + "\\E", "|") + ").*"
result = "(?s)" + prefix + "(" + strictconcat("\\Q" + getADelimiter() + "\\E", "|") + ").*"
}

/** A placeholder tag for a templating engine. */
Expand Down Expand Up @@ -703,7 +703,7 @@ module Templating {
*
* These API nodes are used in the `getTemplateInput` predicate.
*/
overlay[global]
overlay[local?]
private class IncludeFunctionAsEntryPoint extends API::EntryPoint {
IncludeFunctionAsEntryPoint() { this = "IncludeFunctionAsEntryPoint" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ private import semmle.javascript.security.dataflow.CodeInjectionCustomizations
* Module for working with uses of the [Trusted Types API](https://developer.mozilla.org/en-US/docs/Web/API/Trusted_Types_API).
*/
module TrustedTypes {
overlay[local?]
private class TrustedTypesEntry extends API::EntryPoint {
TrustedTypesEntry() { this = "TrustedTypesEntry" }

Expand Down
3 changes: 3 additions & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/Vue.qll
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import semmle.javascript.ViewComponentInput

module Vue {
/** The global variable `Vue`, as an API graph entry point. */
overlay[local?]
private class GlobalVueEntryPoint extends API::EntryPoint {
GlobalVueEntryPoint() { this = "VueEntryPoint" }

Expand All @@ -18,6 +19,7 @@ module Vue {
*
* This `EntryPoint` is used by `SingleFileComponent::getOwnOptions()`.
*/
overlay[local?]
private class VueExportEntryPoint extends API::EntryPoint {
VueExportEntryPoint() { this = "VueExportEntryPoint" }

Expand Down Expand Up @@ -437,6 +439,7 @@ module Vue {
*
* This entry point is used in `SingleFileComponent::getComponentRef()`.
*/
overlay[local?]
private class VueFileImportEntryPoint extends API::EntryPoint {
VueFileImportEntryPoint() { this = "VueFileImportEntryPoint" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
private import javascript

/** Treats `Response` as an entry point for API graphs. */
overlay[local?]
private class ResponseEntryPoint extends API::EntryPoint {
ResponseEntryPoint() { this = "global.Response" }

override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Response") }
}

/** Treats `Headers` as an entry point for API graphs. */
overlay[local?]
private class HeadersEntryPoint extends API::EntryPoint {
HeadersEntryPoint() { this = "global.Headers" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ private predicate areLibrariesCompatible(
}

/** Treats `WebSocket` as an entry point for API graphs. */
overlay[local?]
private class WebSocketEntryPoint extends API::EntryPoint {
WebSocketEntryPoint() { this = "global.WebSocket" }

override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("WebSocket") }
}

/** Treats `SockJS` as an entry point for API graphs. */
overlay[local?]
private class SockJSEntryPoint extends API::EntryPoint {
SockJSEntryPoint() { this = "global.SockJS" }

Expand Down
1 change: 1 addition & 0 deletions javascript/ql/lib/semmle/javascript/frameworks/Webix.qll
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ private import javascript
*/
module Webix {
/** The global variable `webix` as an entry point for API graphs. */
overlay[local?]
private class WebixGlobalEntry extends API::EntryPoint {
WebixGlobalEntry() { this = "WebixGlobalEntry" }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -492,6 +492,7 @@ private predicate invocationMatchesCallSiteFilter(
Specific::invocationMatchesExtraCallSiteFilter(invoke, token)
}

overlay[local?]
private class TypeModelUseEntry extends API::EntryPoint {
private string type;

Expand All @@ -505,6 +506,7 @@ private class TypeModelUseEntry extends API::EntryPoint {
API::Node getNodeForType(string type_) { type = type_ and result = this.getANode() }
}

overlay[local?]
private class TypeModelDefEntry extends API::EntryPoint {
private string type;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ private predicate parseRelevantTypeString(string rawType, string package, string
}

/** Holds if `global` is a global variable referenced via a the `global` package in a CSV row. */
overlay[local]
private predicate isRelevantGlobal(string global) {
exists(AccessPath path, AccessPathToken token |
isRelevantFullPath("global", path) and
Expand All @@ -103,6 +104,7 @@ private predicate isRelevantGlobal(string global) {
}

/** An API graph entry point for global variables mentioned in a model. */
overlay[local?]
private class GlobalApiEntryPoint extends API::EntryPoint {
string global;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ class ClientSideRemoteFlowKind extends string {
* `name` and `address` of global variable `user` should be considered as remote flow sources with
* source type "user input".
*/
overlay[local?]
private class RemoteFlowSourceAccessPath extends JsonString {
string sourceType;

Expand Down Expand Up @@ -167,6 +168,7 @@ private class RemoteFlowSourceAccessPath extends JsonString {
* The global variable referenced by a `RemoteFlowSourceAccessPath`, declared as an API
* entry point.
*/
overlay[local?]
private class ExternalRemoteFlowSourceSpecEntryPoint extends API::EntryPoint {
string name;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ module SecondOrderCommandInjection {
/**
* A sink that invokes a command described by the `VulnerableCommand` class.
*/
overlay[global]
abstract class VulnerableCommandSink extends Sink {
VulnerableCommand cmd;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ module TaintedPath {
* There are currently four flow labels, representing the different combinations of
* normalization and absoluteness.
*/
overlay[global]
abstract class PosixPath extends DataFlow::FlowLabel {
Normalization normalization;
Relativeness relativeness;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ module UnsafeHtmlConstruction {
* A sink for `js/html-constructed-from-input` that constructs some HTML where
* that HTML is later used in `xssSink`.
*/
overlay[global]
abstract class XssSink extends Sink {
DomBasedXss::Sink xssSink;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
overlay[local?]
class CustomEntryPoint extends API::EntryPoint {
CustomEntryPoint() { this = "CustomEntryPoint" }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
testFailures
ambiguousPreferredPredecessor
| pack2/lib.js:1:1:3:1 | def moduleImport("pack2").getMember("exports").getMember("lib").getMember("LibClass").getInstance() |
| pack2/lib.js:8:22:8:34 | def moduleImport("pack2").getMember("exports").getMember("lib").getMember("LibClass").getMember("foo") |
| pack2/main.js:1:1:3:1 | def moduleImport("pack2").getMember("exports").getMember("MainClass").getInstance() |
| pack2/lib.js:1:1:3:1 | instance of class A ... ethod\\n} |
| pack2/lib.js:8:22:8:34 | def function() {} |
| pack2/main.js:1:1:3:1 | instance of class A ... ethod\\n} |
ambiguousSinkName
ambiguousFunctionName
Loading
Loading