mirror of
https://github.com/github/codeql.git
synced 2026-04-28 10:15:14 +02:00
Merge branch 'main' into js/name-resolution-independent-fixes
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: majorAnalysis
|
||||
---
|
||||
* Added support for TypeScript 5.8.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for the `react-relay` library.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* Extraction now supports regular expressions with the `v` flag, using the new operators:
|
||||
- Intersection `&&`
|
||||
- Subtraction `--`
|
||||
- `\q` quoted string
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved the modeling of the `markdown-table` package to ensure it handles nested arrays properly.
|
||||
@@ -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.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for the `@tanstack/vue-query` package.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added taint-steps for `unescape()`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added additional flow step for `unescape()` and `escape()`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added support for the `underscore.string` package.
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved support for `got` package with `Options`, `paginate()` and `extend()`
|
||||
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Improved modeling of the `node:fs` module: `await`-ed calls to `read` and `readFile` are now supported.
|
||||
31
javascript/ql/lib/change-notes/released/2.6.0.md
Normal file
31
javascript/ql/lib/change-notes/released/2.6.0.md
Normal 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.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 2.5.1
|
||||
lastReleaseVersion: 2.6.0
|
||||
|
||||
27
javascript/ql/lib/ext/hana-db-client.model.yml
Normal file
27
javascript/ql/lib/ext/hana-db-client.model.yml
Normal 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']
|
||||
@@ -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
|
||||
|
||||
@@ -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()" }
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user