Merge branch 'main' into js/name-resolution-independent-fixes

This commit is contained in:
Asger F
2025-04-02 14:15:44 +02:00
committed by GitHub
778 changed files with 26643 additions and 8489 deletions

View File

@@ -1,3 +1,35 @@
## 2.6.0
### New Features
* Extraction now supports regular expressions with the `v` flag, using the new operators:
- Intersection `&&`
- Subtraction `--`
- `\q` quoted string
### Major Analysis Improvements
* Added support for TypeScript 5.8.
### Minor Analysis Improvements
* Added support for additional `fs-extra` methods as sinks in path-injection queries.
* Added support for the newer version of `Hapi` with the `@hapi/hapi` import and `server` function.
* Improved modeling of the `node:fs` module: `await`-ed calls to `read` and `readFile` are now supported.
* Added support for the `@sap/hana-client`, `@sap/hdbext` and `hdb` packages.
* Enhanced `axios` support with new methods (`postForm`, `putForm`, `patchForm`, `getUri`, `create`) and added support for `interceptors.request` and `interceptors.response`.
* Improved support for `got` package with `Options`, `paginate()` and `extend()`
* Added support for the `ApolloServer` class from `@apollo/server` and similar packages. In particular, the incoming data in a GraphQL resolver is now seen as a source of untrusted user input.
* Improved support for `superagent` to handle the case where the package is directly called as a function, or via the `.del()` or `.agent()` method.
* Added support for the `underscore.string` package.
* Added additional flow step for `unescape()` and `escape()`.
* Added support for the `@tanstack/vue-query` package.
* Added taint-steps for `unescape()`.
* Added support for the `@tanstack/angular-query-experimental` package.
* Improved support for the `@angular/common/http` package, detecting outgoing HTTP requests in more cases.
* Improved the modeling of the `markdown-table` package to ensure it handles nested arrays properly.
* Added support for the `react-relay` library.
## 2.5.1
No user-facing changes.

View File

@@ -1,4 +0,0 @@
---
category: majorAnalysis
---
* Added support for TypeScript 5.8.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added support for the `react-relay` library.

View File

@@ -1,7 +0,0 @@
---
category: feature
---
* Extraction now supports regular expressions with the `v` flag, using the new operators:
- Intersection `&&`
- Subtraction `--`
- `\q` quoted string

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved the modeling of the `markdown-table` package to ensure it handles nested arrays properly.

View File

@@ -1,5 +0,0 @@
---
category: minorAnalysis
---
* Added support for the `@tanstack/angular-query-experimental` package.
* Improved support for the `@angular/common/http` package, detecting outgoing HTTP requests in more cases.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added support for the `@tanstack/vue-query` package.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added taint-steps for `unescape()`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added additional flow step for `unescape()` and `escape()`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added support for the `underscore.string` package.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Added support for the `ApolloServer` class from `@apollo/server` and similar packages. In particular, the incoming data in a GraphQL resolver is now seen as a source of untrusted user input.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved support for `superagent` to handle the case where the package is directly called as a function, or via the `.del()` or `.agent()` method.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
Enhanced `axios` support with new methods (`postForm`, `putForm`, `patchForm`, `getUri`, `create`) and added support for `interceptors.request` and `interceptors.response`.

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved support for `got` package with `Options`, `paginate()` and `extend()`

View File

@@ -1,4 +0,0 @@
---
category: minorAnalysis
---
* Improved modeling of the `node:fs` module: `await`-ed calls to `read` and `readFile` are now supported.

View File

@@ -0,0 +1,31 @@
## 2.6.0
### New Features
* Extraction now supports regular expressions with the `v` flag, using the new operators:
- Intersection `&&`
- Subtraction `--`
- `\q` quoted string
### Major Analysis Improvements
* Added support for TypeScript 5.8.
### Minor Analysis Improvements
* Added support for additional `fs-extra` methods as sinks in path-injection queries.
* Added support for the newer version of `Hapi` with the `@hapi/hapi` import and `server` function.
* Improved modeling of the `node:fs` module: `await`-ed calls to `read` and `readFile` are now supported.
* Added support for the `@sap/hana-client`, `@sap/hdbext` and `hdb` packages.
* Enhanced `axios` support with new methods (`postForm`, `putForm`, `patchForm`, `getUri`, `create`) and added support for `interceptors.request` and `interceptors.response`.
* Improved support for `got` package with `Options`, `paginate()` and `extend()`
* Added support for the `ApolloServer` class from `@apollo/server` and similar packages. In particular, the incoming data in a GraphQL resolver is now seen as a source of untrusted user input.
* Improved support for `superagent` to handle the case where the package is directly called as a function, or via the `.del()` or `.agent()` method.
* Added support for the `underscore.string` package.
* Added additional flow step for `unescape()` and `escape()`.
* Added support for the `@tanstack/vue-query` package.
* Added taint-steps for `unescape()`.
* Added support for the `@tanstack/angular-query-experimental` package.
* Improved support for the `@angular/common/http` package, detecting outgoing HTTP requests in more cases.
* Improved the modeling of the `markdown-table` package to ensure it handles nested arrays properly.
* Added support for the `react-relay` library.

View File

@@ -1,2 +1,2 @@
---
lastReleaseVersion: 2.5.1
lastReleaseVersion: 2.6.0

View File

@@ -0,0 +1,27 @@
extensions:
- addsTo:
pack: codeql/javascript-all
extensible: sinkModel
data:
- ["@sap/hana-client", "Member[createConnection].ReturnValue.Member[exec,prepare].Argument[0]", "sql-injection"]
- ["hdb.Client", "Member[exec,prepare,execute].Argument[0]", "sql-injection"]
- ["@sap/hdbext", "Member[loadProcedure].Argument[2]", "sql-injection"]
- ["@sap/hana-client/extension/Stream", "Member[createProcStatement].Argument[1]", "sql-injection"]
- addsTo:
pack: codeql/javascript-all
extensible: typeModel
data:
- ["hdb.Client", "hdb", "Member[createClient].ReturnValue"]
- ["hdb.Client", "@sap/hdbext", "Member[middleware].ReturnValue.GuardedRouteHandler.Parameter[0].Member[db]"]
- addsTo:
pack: codeql/javascript-all
extensible: sourceModel
data:
- ['@sap/hana-client', 'Member[createConnection].ReturnValue.Member[exec].Argument[1].Parameter[1]', 'database-access-result']
- ['@sap/hana-client', 'Member[createConnection].ReturnValue.Member[prepare].ReturnValue.Member[execBatch,exec,execQuery].Argument[1].Parameter[1]', 'database-access-result']
- ['hdb.Client', 'Member[exec,execute].Argument[1..2].Parameter[1]', 'database-access-result']
- ['hdb.Client', 'Member[prepare].Argument[1].Parameter[1].Member[exec].Argument[1].Parameter[2..]', 'database-access-result']
- ["@sap/hana-client/extension/Stream", "Member[createProcStatement].Argument[2].Parameter[1].Member[exec].Argument[1].Parameter[2..]", "database-access-result"]
- ['@sap/hdbext', 'Member[loadProcedure].Argument[3].Parameter[1].Argument[2].Parameter[2..]', 'database-access-result']

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all
version: 2.5.2-dev
version: 2.6.1-dev
groups: javascript
dbscheme: semmlecode.javascript.dbscheme
extractor: javascript

View File

@@ -318,6 +318,11 @@ module API {
Node getParameter(int i) {
Stages::ApiStage::ref() and
result = this.getASuccessor(Label::parameter(i))
or
exists(int spreadIndex, string arrayProp |
result = this.getASuccessor(Label::spreadArgument(spreadIndex)).getMember(arrayProp) and
i = spreadIndex + arrayProp.toInt()
)
}
/**
@@ -860,6 +865,23 @@ module API {
.getStaticMember(name, DataFlow::MemberKind::getter())
.getAReturn()
)
or
// Handle rest parameters escaping into external code. For example:
//
// function foo(...rest) {
// externalFunc(rest);
// }
//
// Here, 'rest' reaches a def-node at the call to externalFunc, so we need to ensure
// the arguments passed to 'foo' are stored in the 'rest' array.
exists(Function fun, DataFlow::InvokeNode invoke, int argIndex, Parameter rest |
fun.getRestParameter() = rest and
rest.flow() = pred and
invoke.getACallee() = fun and
invoke.getArgument(argIndex) = rhs and
argIndex >= rest.getIndex() and
lbl = Label::member((argIndex - rest.getIndex()).toString())
)
)
or
exists(DataFlow::ClassNode cls, string name |
@@ -888,6 +910,11 @@ module API {
i = -1 and lbl = Label::receiver()
)
or
exists(int i |
spreadArgumentPassing(base, i, rhs) and
lbl = Label::spreadArgument(i)
)
or
exists(DataFlow::SourceNode src, DataFlow::PropWrite pw |
use(base, src) and pw = trackUseNode(src).getAPropertyWrite() and rhs = pw.getRhs()
|
@@ -931,6 +958,29 @@ module API {
)
}
pragma[nomagic]
private int firstSpreadIndex(InvokeExpr expr) {
result = min(int i | expr.getArgument(i) instanceof SpreadElement)
}
pragma[nomagic]
private InvokeExpr getAnInvocationWithSpread(DataFlow::SourceNode node, int i) {
result = node.getAnInvocation().asExpr() and
i = firstSpreadIndex(result)
}
private predicate spreadArgumentPassing(TApiNode base, int i, DataFlow::Node spreadArray) {
exists(
DataFlow::Node use, DataFlow::SourceNode pred, int bound, InvokeExpr invoke, int spreadPos
|
use(base, use) and
pred = trackUseNode(use, _, bound, "") and
invoke = getAnInvocationWithSpread(pred, spreadPos) and
spreadArray = invoke.getArgument(spreadPos).(SpreadElement).getOperand().flow() and
i = bound + spreadPos
)
}
/**
* Holds if `rhs` is the right-hand side of a definition of node `nd`.
*/
@@ -1579,6 +1629,9 @@ module API {
/** Gets the edge label for the receiver. */
LabelReceiver receiver() { any() }
/** Gets the edge label for a spread argument passed at index `i`. */
LabelSpreadArgument spreadArgument(int i) { result.getIndex() = i }
/** Gets the `return` edge label. */
LabelReturn return() { any() }
@@ -1628,6 +1681,7 @@ module API {
} or
MkLabelReceiver() or
MkLabelReturn() or
MkLabelSpreadArgument(int index) { index = [0 .. 10] } or
MkLabelDecoratedClass() or
MkLabelDecoratedMember() or
MkLabelDecoratedParameter() or
@@ -1743,6 +1797,21 @@ module API {
override string toString() { result = "getReceiver()" }
}
/** A label representing an array passed as a spread argument at a given index. */
class LabelSpreadArgument extends ApiLabel, MkLabelSpreadArgument {
private int index;
LabelSpreadArgument() { this = MkLabelSpreadArgument(index) }
/** Gets the argument index at which the spread argument appears. */
int getIndex() { result = index }
override string toString() {
// Note: This refers to the internal edge that has no corresponding method on API::Node
result = "getSpreadArgument(" + index + ")"
}
}
/** A label for a function that is a wrapper around another function. */
class LabelForwardingFunction extends ApiLabel, MkLabelForwardingFunction {
override string toString() { result = "getForwardingFunction()" }

View File

@@ -254,6 +254,12 @@ private module Cached {
cached
predicate invocation(DataFlow::SourceNode func, DataFlow::InvokeNode invoke) {
hasLocalSource(invoke.getCalleeNode(), func)
or
exists(ClassDefinition cls, SuperCall call |
hasLocalSource(cls.getSuperClass().flow(), func) and
call.getBinder() = cls.getConstructor().getBody() and
invoke = call.flow()
)
}
/**

View File

@@ -56,14 +56,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig {
predicate hasCfgNode(js::BasicBlock bb, int i) { this = bb.getNode(i) }
}
predicate ssaDefAssigns(WriteDefinition def, Expr value) {
// This library only handles use-use flow after a post-update, there are no definitions, only uses.
none()
}
class Parameter = js::Parameter;
predicate ssaDefInitializesParam(WriteDefinition def, Parameter p) {
predicate ssaDefHasSource(WriteDefinition def) {
// This library only handles use-use flow after a post-update, there are no definitions, only uses.
none()
}
@@ -97,7 +90,7 @@ module SsaDataflowInput implements DataFlowIntegrationInputSig {
}
pragma[inline]
predicate guardControlsBlock(Guard guard, js::BasicBlock bb, boolean branch) {
predicate guardDirectlyControlsBlock(Guard guard, js::BasicBlock bb, boolean branch) {
exists(js::ConditionGuardNode g |
g.getTest() = guard and
g.dominates(bb) and

View File

@@ -11,8 +11,8 @@ module Hapi {
*/
class ServerDefinition extends Http::Servers::StandardServerDefinition, DataFlow::Node {
ServerDefinition() {
// `server = new Hapi.Server()`
this = DataFlow::moduleMember("hapi", "Server").getAnInstantiation()
// `server = new Hapi.Server()`, `server = Hapi.server()`
this = DataFlow::moduleMember(["hapi", "@hapi/hapi"], ["Server", "server"]).getAnInvocation()
or
// `server = Glue.compose(manifest, composeOptions)`
this = DataFlow::moduleMember("@hapi/glue", "compose").getAnInvocation()

View File

@@ -434,7 +434,7 @@ module NodeJSLib {
* method might represent a file path.
*/
private predicate fsExtraExtensionFileParam(string methodName, int i) {
methodName = ["copy", "copySync", "copyFile"] and i = [0, 1]
methodName = ["copy", "copySync", "copyFile", "cp", "copyFileSync", "cpSync"] and i = [0, 1]
or
methodName = ["move", "moveSync"] and i = [0, 1]
or
@@ -450,10 +450,13 @@ module NodeJSLib {
or
methodName = ["readJson", "readJSON", "readJsonSync", "readJSONSync"] and i = 0
or
methodName = ["remove", "removeSync"] and i = 0
methodName = ["remove", "removeSync", "rmSync", "rm", "rmdir", "rmdirSync"] and i = 0
or
methodName =
["outputJSON", "outputJson", "writeJSON", "writeJson", "writeJSONSync", "writeJsonSync"] and
[
"outputJSON", "outputJson", "writeJSON", "writeJson", "writeJSONSync", "writeJsonSync",
"outputJSONSync", "outputJsonSync"
] and
i = 0
or
methodName = ["ensureFile", "ensureFileSync"] and i = 0
@@ -462,9 +465,15 @@ module NodeJSLib {
or
methodName = ["ensureSymlink", "ensureSymlinkSync"] and i = [0, 1]
or
methodName = ["emptyDir", "emptyDirSync"] and i = 0
methodName = ["emptyDir", "emptyDirSync", "emptydir", "emptydirSync"] and i = 0
or
methodName = ["pathExists", "pathExistsSync"] and i = 0
or
methodName = ["lutimes", "lutimesSync"] and i = 0
or
methodName =
["opendir", "opendirSync", "openAsBlob", "statfs", "statfsSync", "open", "openSync"] and
i = 0
}
/**
@@ -592,6 +601,13 @@ module NodeJSLib {
}
}
/** A vectored write to the file system using `writev` or `writevSync` methods. */
private class NodeJSFileSystemVectorWrite extends FileSystemWriteAccess, NodeJSFileSystemAccess {
NodeJSFileSystemVectorWrite() { methodName = ["writev", "writevSync"] }
override DataFlow::Node getADataNode() { result = this.getArgument(1) }
}
/** A file system read. */
private class NodeJSFileSystemAccessRead extends FileSystemReadAccess, NodeJSFileSystemAccess {
NodeJSFileSystemAccessRead() { methodName = ["read", "readSync", "readFile", "readFileSync"] }
@@ -619,6 +635,22 @@ module NodeJSLib {
}
}
/** A vectored read to the file system. */
private class NodeJSFileSystemAccessVectorRead extends FileSystemReadAccess,
NodeJSFileSystemAccess
{
NodeJSFileSystemAccessVectorRead() { methodName = ["readv", "readvSync"] }
override DataFlow::Node getADataNode() {
result = this.getArgument(1)
or
exists(DataFlow::ArrayCreationNode array |
array.flowsTo(this.getArgument(1)) and
result = array.getAnElement()
)
}
}
/**
* A write to the file system, using a stream.
*/

View File

@@ -184,6 +184,20 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathTokenBase token) {
or
token.getName() = "DecoratedParameter" and
result = node.getADecoratedParameter()
or
token.getName() = "GuardedRouteHandler" and
result = getAGuardedRouteHandlerApprox(node)
}
bindingset[node]
pragma[inline_late]
private API::Node getAGuardedRouteHandlerApprox(API::Node node) {
// For now just get any routing node with the same root (i.e. the same web app), as
// there are some known performance issues when checking if it is actually guarded by the given node.
exists(JS::Routing::Node root |
root = JS::Routing::getNode(node.getAValueReachableFromSource()).getRootNode() and
root = JS::Routing::getNode(result.asSink()).getRootNode()
)
}
/**
@@ -317,7 +331,7 @@ predicate isExtraValidTokenNameInIdentifyingAccessPath(string name) {
[
"Member", "AnyMember", "Instance", "Awaited", "ArrayElement", "Element", "MapValue",
"NewCall", "Call", "DecoratedClass", "DecoratedMember", "DecoratedParameter",
"WithStringArgument"
"WithStringArgument", "GuardedRouteHandler"
]
}
@@ -329,7 +343,7 @@ predicate isExtraValidNoArgumentTokenInIdentifyingAccessPath(string name) {
name =
[
"AnyMember", "Instance", "Awaited", "ArrayElement", "Element", "MapValue", "NewCall", "Call",
"DecoratedClass", "DecoratedMember", "DecoratedParameter"
"DecoratedClass", "DecoratedMember", "DecoratedParameter", "GuardedRouteHandler"
]
}