Merge remote-tracking branch 'origin/main' into dbartol/mergeback-3.10

This commit is contained in:
Dave Bartolomeo
2023-07-06 10:00:46 -04:00
1654 changed files with 47750 additions and 21714 deletions

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* Added models for the Webix Framework.

View File

@@ -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

View File

@@ -6,6 +6,7 @@ extractor: javascript
library: true
upgrades: upgrades
dependencies:
codeql/mad: ${workspace}
codeql/regex: ${workspace}
codeql/tutorial: ${workspace}
codeql/util: ${workspace}

View File

@@ -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"])
)
}

View File

@@ -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() =

View File

@@ -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

View File

@@ -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
)
}

View 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()
}
}

View File

@@ -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()
}
}

View File

@@ -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 + "%")
)
}
/**

View File

@@ -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.
*/

View File

@@ -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.
*/

View File

@@ -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"
}
}

View File

@@ -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