mirror of
https://github.com/github/codeql.git
synced 2026-04-29 02:35:15 +02:00
Merge remote-tracking branch 'origin/main' into dbartol/mergeback-3.10
This commit is contained in:
4
javascript/ql/lib/change-notes/2023-06-22-webix.md
Normal file
4
javascript/ql/lib/change-notes/2023-06-22-webix.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Added models for the Webix Framework.
|
||||
@@ -134,6 +134,7 @@ import semmle.javascript.frameworks.TrustedTypes
|
||||
import semmle.javascript.frameworks.UriLibraries
|
||||
import semmle.javascript.frameworks.Vue
|
||||
import semmle.javascript.frameworks.Vuex
|
||||
import semmle.javascript.frameworks.Webix
|
||||
import semmle.javascript.frameworks.WebSocket
|
||||
import semmle.javascript.frameworks.XmlParsers
|
||||
import semmle.javascript.frameworks.xUnit
|
||||
|
||||
@@ -6,6 +6,7 @@ extractor: javascript
|
||||
library: true
|
||||
upgrades: upgrades
|
||||
dependencies:
|
||||
codeql/mad: ${workspace}
|
||||
codeql/regex: ${workspace}
|
||||
codeql/tutorial: ${workspace}
|
||||
codeql/util: ${workspace}
|
||||
|
||||
@@ -96,7 +96,10 @@ private class ExtendCallDeep extends ExtendCall {
|
||||
callee = LodashUnderscore::member("merge") or
|
||||
callee = LodashUnderscore::member("mergeWith") or
|
||||
callee = LodashUnderscore::member("defaultsDeep") or
|
||||
callee = AngularJS::angular().getAPropertyRead("merge")
|
||||
callee = AngularJS::angular().getAPropertyRead("merge") or
|
||||
callee =
|
||||
[DataFlow::moduleImport("webix"), DataFlow::globalVarRef("webix")]
|
||||
.getAPropertyRead(["extend", "copy"])
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,6 @@ module InclusionTest {
|
||||
inner.getContainerNode().getALocalSource() = DataFlow::parameterNode(callee.getAParameter())
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getContainerNode() {
|
||||
exists(int arg |
|
||||
inner.getContainerNode().getALocalSource() =
|
||||
@@ -78,7 +77,6 @@ module InclusionTest {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getContainedNode() {
|
||||
exists(int arg |
|
||||
inner.getContainedNode().getALocalSource() =
|
||||
|
||||
@@ -67,7 +67,6 @@ module StringOps {
|
||||
inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getAParameter()
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getBaseString() {
|
||||
exists(int arg |
|
||||
inner.getBaseString().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and
|
||||
@@ -75,7 +74,6 @@ module StringOps {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getSubstring() {
|
||||
exists(int arg |
|
||||
inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and
|
||||
@@ -294,7 +292,6 @@ module StringOps {
|
||||
inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getAParameter()
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getBaseString() {
|
||||
exists(int arg |
|
||||
inner.getBaseString().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and
|
||||
@@ -302,7 +299,6 @@ module StringOps {
|
||||
)
|
||||
}
|
||||
|
||||
pragma[assume_small_delta]
|
||||
override DataFlow::Node getSubstring() {
|
||||
exists(int arg |
|
||||
inner.getSubstring().getALocalSource().getEnclosingExpr() = callee.getParameter(arg) and
|
||||
|
||||
@@ -289,7 +289,8 @@ module Vuex {
|
||||
or
|
||||
exists(string base, string prop |
|
||||
result = stateRefByAccessPath(base).getMember(prop) and
|
||||
path = appendToNamespace(base, prop)
|
||||
path = appendToNamespace(base, prop) and
|
||||
path.length() < 100
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
23
javascript/ql/lib/semmle/javascript/frameworks/Webix.qll
Normal file
23
javascript/ql/lib/semmle/javascript/frameworks/Webix.qll
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Provides classes and predicates for working with the `webix` library.
|
||||
*/
|
||||
|
||||
private import javascript
|
||||
|
||||
/**
|
||||
* Provides classes and predicates for working with the `webix` library.
|
||||
*/
|
||||
module Webix {
|
||||
/** The global variable `webix` as an entry point for API graphs. */
|
||||
private class WebixGlobalEntry extends API::EntryPoint {
|
||||
WebixGlobalEntry() { this = "WebixGlobalEntry" }
|
||||
|
||||
override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("webix") }
|
||||
}
|
||||
|
||||
/** Gets a reference to the Webix package. */
|
||||
API::Node webix() {
|
||||
result = API::moduleImport("webix") or
|
||||
result = any(WebixGlobalEntry w).getANode()
|
||||
}
|
||||
}
|
||||
@@ -643,6 +643,15 @@ module ModelOutput {
|
||||
baseNode = getInvocationFromPath(type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if a `baseNode` is a callable identified by the `type,path` part of a summary row.
|
||||
*/
|
||||
cached
|
||||
predicate resolvedSummaryRefBase(string type, string path, API::Node baseNode) {
|
||||
summaryModel(type, path, _, _, _) and
|
||||
baseNode = getNodeFromPath(type, path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is seen as an instance of `type` due to a type definition
|
||||
* contributed by a CSV model.
|
||||
@@ -653,6 +662,17 @@ module ModelOutput {
|
||||
|
||||
import Cached
|
||||
import Specific::ModelOutputSpecific
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, kind) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, kind) }
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, kind) }
|
||||
}
|
||||
|
||||
private module KindVal = SharedModelVal::KindValidation<KindValConfig>;
|
||||
|
||||
/**
|
||||
* Gets an error message relating to an invalid CSV row in a model.
|
||||
@@ -698,5 +718,8 @@ module ModelOutput {
|
||||
not isValidNoArgumentTokenInIdentifyingAccessPath(token.getName()) and
|
||||
result = "Invalid token '" + token + "' is missing its arguments, in access path: " + path
|
||||
)
|
||||
or
|
||||
// Check for invalid model kinds
|
||||
result = KindVal::getInvalidModelKind()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,14 @@ private class DangerousPrefix extends string {
|
||||
this = "<!--" or
|
||||
this = "<" + ["iframe", "script", "cript", "scrip", "style"]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a character that is important to the dangerous prefix.
|
||||
* That is, a char that should be mentioned in a regular expression that explicitly sanitizes the dangerous prefix.
|
||||
*/
|
||||
string getAnImportantChar() {
|
||||
if this = ["/..", "../"] then result = ["/", "."] else result = "<"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +70,11 @@ private DangerousPrefixSubstring getADangerousMatchedChar(EmptyReplaceRegExpTerm
|
||||
*/
|
||||
private DangerousPrefix getADangerousMatchedPrefix(EmptyReplaceRegExpTerm t) {
|
||||
result = getADangerousMatchedPrefixSubstring(t) and
|
||||
not exists(EmptyReplaceRegExpTerm pred | pred = t.getPredecessor+() and not pred.isNullable())
|
||||
not exists(EmptyReplaceRegExpTerm pred | pred = t.getPredecessor+() and not pred.isNullable()) and
|
||||
// the regex must explicitly mention a char important to the prefix.
|
||||
forex(string char | char = result.getAnImportantChar() |
|
||||
t.getRootTerm().getAChild*().(RegExpConstant).getValue().matches("%" + char + "%")
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -120,6 +120,22 @@ module TaintedObject {
|
||||
override predicate sanitizes(boolean outcome, Expr e) { e = x and outcome = polarity }
|
||||
}
|
||||
|
||||
/** A guard that checks whether an input a valid string identifier using `mongoose.Types.ObjectId.isValid` */
|
||||
class ObjectIdGuard extends SanitizerGuard instanceof API::CallNode {
|
||||
ObjectIdGuard() {
|
||||
this =
|
||||
API::moduleImport("mongoose")
|
||||
.getMember("Types")
|
||||
.getMember("ObjectId")
|
||||
.getMember("isValid")
|
||||
.getACall()
|
||||
}
|
||||
|
||||
override predicate sanitizes(boolean outcome, Expr e, FlowLabel lbl) {
|
||||
e = super.getAnArgument().asExpr() and outcome = true and lbl = label()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer guard that validates an input against a JSON schema.
|
||||
*/
|
||||
|
||||
@@ -312,6 +312,13 @@ module CodeInjection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value interpreted as code by the `webix` library.
|
||||
*/
|
||||
class WebixExec extends Sink {
|
||||
WebixExec() { this = Webix::webix().getMember("exec").getParameter(0).asSink() }
|
||||
}
|
||||
|
||||
/** A sink for code injection via template injection. */
|
||||
abstract private class TemplateSink extends Sink {
|
||||
deprecated override string getMessageSuffix() {
|
||||
@@ -419,6 +426,18 @@ module CodeInjection {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A value interpreted as a template by the `webix` library.
|
||||
*/
|
||||
class WebixTemplateSink extends TemplateSink {
|
||||
WebixTemplateSink() {
|
||||
this = Webix::webix().getMember("ui").getParameter(0).getMember("template").asSink()
|
||||
or
|
||||
this =
|
||||
Webix::webix().getMember("ui").getParameter(0).getMember("template").getReturn().asSink()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to JSON.stringify() seen as a sanitizer.
|
||||
*/
|
||||
|
||||
@@ -171,5 +171,9 @@ module PrototypePollution {
|
||||
call.isDeep() and
|
||||
call = AngularJS::angular().getAMemberCall("merge") and
|
||||
id = "angular"
|
||||
or
|
||||
call.isDeep() and
|
||||
call = Webix::webix().getMember(["extend", "copy"]).getACall() and
|
||||
id = "webix"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,6 @@ module SecondOrderCommandInjection {
|
||||
int cmdIndex;
|
||||
int argIndex;
|
||||
|
||||
pragma[assume_small_delta]
|
||||
IndirectCmdFunc() {
|
||||
exists(CommandExecutingCall call |
|
||||
this.getParameter(cmdIndex).flowsTo(call.getCommandArg()) and
|
||||
|
||||
Reference in New Issue
Block a user