10000 Improve data flow in the `async` package by Vasco-jofra · Pull Request #19770 · github/codeql · GitHub
[go: up one dir, main page]

Skip to content

Improve data flow in the async package #19770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
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
Prev Previous commit
Merge SummarizedCallable into single class
  • Loading branch information
Vasco-jofra committed Jun 26, 2025
commit 575da5c31c8909a99a64dc20571bb7c23aa22635
75 changes: 23 additions & 52 deletions javascript/ql/lib/semmle/javascript/frameworks/AsyncPackage.qll
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,24 @@ module AsyncPackage {
result = DataFlow::moduleMember("async-es", name)
}

/**
* Gets a reference to the given member or one of its `Limit` or `Series` variants.
*
* For example, `memberVariant("map")` finds references to `map`, `mapLimit`, and `mapSeries`.
*/
DataFlow::SourceNode memberVariant(string name) {
result = member(name) or
result = member(name + "Limit") or
result = member(name + "Series")
}

/**
* Gets `Limit` or `Series` name variants for a given member name.
*
* For example, `memberNameVariant("map")` returns `map`, `mapLimit`, and `mapSeries`.
*/
bindingset[name]
string memberNameVariant(string name) {
private string memberNameVariant(string name) {
result = name or
result = name + "Limit" or
result = name + "Series"
Expand Down Expand Up @@ -161,63 +172,23 @@ module AsyncPackage {
DataFlow::FunctionNode getFinalCallback() { result = this.getCallback(finalCallbackIndex) }
}

/**
* An IterationCall with its iterator callback at index 1
*/
private class IterationCallCallbacksFirstArg extends IterationCall {
IterationCallCallbacksFirstArg() { this.getIteratorCallbackIndex() = 1 }
}

/**
* An IterationCall with its iterator callback at index 2
*/
private class IterationCallCallbacksSecondArg extends IterationCall {
IterationCallCallbacksSecondArg() { this.getIteratorCallbackIndex() = 2 }
}

/**
* The model with the iteratorCallbackIndex abstracted
*/
bindingset[iteratorCallbackIndex]
private predicate iterationCallPropagatesFlow(
string input, string output, boolean preservesValue, int iteratorCallbackIndex
) {
preservesValue = true and
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement", "AnyMember"] and
output = "Argument[" + iteratorCallbackIndex + "].Parameter[0]"
}
private class IterationCallFlowSummary extends DataFlow::SummarizedCallable {
private int callbackArgIndex;

/**
* A taint step from the collection into the iterator callback (at index 1) of an iteration call.
*
* For example: `data -> item` in `async.each(data, (item, cb) => {})`.
*/
class IterationCallCallbacksFirstArgFlowSummary extends DataFlow::SummarizedCallable {
IterationCallCallbacksFirstArgFlowSummary() { this = "async.[IterationCallCallbacksFirstArg]" }

override DataFlow::InvokeNode getACallSimple() {
result instanceof IterationCallCallbacksFirstArg
IterationCallFlowSummary() {
this = "async.IteratorCall(callbackArgIndex=" + callbackArgIndex + ")" and
callbackArgIndex in [1 .. 3]
}

override predicate propagatesFlow(string input, string output, boolean preservesValue) {
iterationCallPropagatesFlow(input, output, preservesValue, 1)
}
}

/**
* A taint step from the collection into the iterator callback (at index 2) of an iteration call.
*
* For example: `data -> item` in `async.eachLimit(data, 1, (item, cb) => {})`.
*/
class IterationCallCallbacksSecondArgFlowSummary extends DataFlow::SummarizedCallable {
IterationCallCallbacksSecondArgFlowSummary() { this = "async.[IterationCallCallbackSecondArg]" }

override DataFlow::InvokeNode getACallSimple() {
result instanceof IterationCallCallbacksSecondArg
result instanceof IterationCall and
result.(IterationCall).getIteratorCallbackIndex() = callbackArgIndex
}

override predicate propagatesFlow(string input, string output, boolean preservesValue) {
iterationCallPropagatesFlow(input, output, preservesValue, 2)
preservesValue = true and
input = "Argument[0]." + ["ArrayElement", "SetElement", "IteratorElement", "AnyMember"] and
output = "Argument[" + callbackArgIndex + "].Parameter[0]"
}
}

Expand Down Expand Up @@ -248,7 +219,7 @@ module AsyncPackage {
*
* For example: `data -> result` in `async.sortBy(data, orderingFn, (err, result) => {})`.
*/
class IterationPreserveTaintStepFlowSummary extends DataFlow::SummarizedCallable {
private class IterationPreserveTaintStepFlowSummary extends DataFlow::SummarizedCallable {
IterationPreserveTaintStepFlowSummary() { this = "async.sortBy" }

override DataFlow::InvokeNode getACallSimple() {
Expand Down
0