diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index 17e00fa022c0..093187eb8651 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: csharp library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll index 46a19828a814..501b140b8860 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/ExternalFlow.qll @@ -95,6 +95,7 @@ private import internal.DataFlowPublic private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific +private import codeql.mad.ModelValidation as SharedModelVal /** Holds if a source model exists for the given parameters. */ predicate sourceModel = Extensions::sourceModel/9; @@ -204,30 +205,18 @@ module ModelValidation { ) } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) - or - exists(string kind | sinkModel(_, _, _, _, _, _, _, kind, _) | - not kind = - ["code-injection", "sql-injection", "js-injection", "html-injection", "file-content-store"] and - not kind.matches("encryption-%") and - result = "Invalid kind \"" + kind + "\" in sink model." - ) - or - exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) | - not kind = ["local", "remote", "file", "file-write"] and - result = "Invalid kind \"" + kind + "\" in source model." - ) - or - exists(string kind | neutralModel(_, _, _, _, kind, _) | - not kind = ["summary", "source", "sink"] and - result = "Invalid kind \"" + kind + "\" in neutral model." - ) + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } + + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } + + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } + + predicate neutralKind(string kind) { neutralModel(_, _, _, _, kind, _) } } + private module KindVal = SharedModelVal::KindValidation; + private string getInvalidModelSignature() { exists( string pred, string namespace, string type, string name, string signature, string ext, @@ -269,7 +258,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } diff --git a/go/ql/lib/qlpack.yml b/go/ql/lib/qlpack.yml index 287c27187e3a..271e9e133853 100644 --- a/go/ql/lib/qlpack.yml +++ b/go/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: go library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} dataExtensions: diff --git a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll index 0c6ee1c31347..4306471893a8 100644 --- a/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll +++ b/go/ql/lib/semmle/go/dataflow/ExternalFlow.qll @@ -68,6 +68,7 @@ private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific private import internal.AccessPathSyntax private import FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A module importing the frameworks that provide external flow data, @@ -188,13 +189,16 @@ module ModelValidation { ) } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) + 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; + private string getInvalidModelSignature() { exists( string pred, string package, string type, string name, string signature, string ext, @@ -231,7 +235,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } diff --git a/java/ql/lib/qlpack.yml b/java/ql/lib/qlpack.yml index ada2ac9e999f..c917caf0641d 100644 --- a/java/ql/lib/qlpack.yml +++ b/java/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: java library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/typetracking: ${workspace} diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll index bb8485cd6017..c04126d5f7f6 100644 --- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll @@ -87,6 +87,7 @@ private import internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific private import internal.AccessPathSyntax private import ExternalFlowExtensions as Extensions private import FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A class for activating additional model rows. @@ -265,87 +266,18 @@ module ModelValidation { ) } - private class OutdatedSinkKind extends string { - OutdatedSinkKind() { - this = - [ - "sql", "url-redirect", "xpath", "ssti", "logging", "groovy", "jexl", "mvel", "xslt", - "ldap", "pending-intent-sent", "intent-start", "set-hostname-verifier", - "header-splitting", "xss", "write-file", "create-file", "read-file", "open-url", - "jdbc-url" - ] - } - - private string replacementKind() { - this = ["sql", "xpath", "groovy", "jexl", "mvel", "xslt", "ldap"] and - result = this + "-injection" - or - this = "url-redirect" and result = "url-redirection" - or - this = "ssti" and result = "template-injection" - or - this = "logging" and result = "log-injection" - or - this = "pending-intent-sent" and result = "pending-intents" - or - this = "intent-start" and result = "intent-redirection" - or - this = "set-hostname-verifier" and result = "hostname-verification" - or - this = "header-splitting" and result = "response-splitting" - or - this = "xss" and result = "html-injection\" or \"js-injection" - or - this = "write-file" and result = "file-content-store" - or - this = ["create-file", "read-file"] and result = "path-injection" - or - this = ["open-url", "jdbc-url"] and result = "request-forgery" - } + private module KindValConfig implements SharedModelVal::KindValidationConfigSig { + predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _) } - string outdatedMessage() { - result = - "The kind \"" + this + "\" is outdated. Use \"" + this.replacementKind() + "\" instead." - } - } + predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _) } - private string getInvalidModelKind() { - exists(string kind | summaryModel(_, _, _, _, _, _, _, _, kind, _) | - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) - or - exists(string kind, string msg | sinkModel(_, _, _, _, _, _, _, kind, _) | - not kind = - [ - "request-forgery", "jndi-injection", "ldap-injection", "sql-injection", "log-injection", - "mvel-injection", "xpath-injection", "groovy-injection", "html-injection", "js-injection", - "ognl-injection", "intent-redirection", "pending-intents", "url-redirection", - "path-injection", "file-content-store", "hostname-verification", "response-splitting", - "information-leak", "xslt-injection", "jexl-injection", "bean-validation", - "template-injection", "fragment-injection", "command-injection" - ] and - not kind.matches("regex-use%") and - not kind.matches("qltest%") and - msg = "Invalid kind \"" + kind + "\" in sink model." and - // The part of this message that refers to outdated sink kinds can be deleted after June 1st, 2024. - if kind instanceof OutdatedSinkKind - then result = msg + " " + kind.(OutdatedSinkKind).outdatedMessage() - else result = msg - ) - or - exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) | - not kind = ["remote", "contentprovider", "android-external-storage-dir"] and - not kind.matches("qltest%") and - result = "Invalid kind \"" + kind + "\" in source model." - ) - or - exists(string kind | neutralModel(_, _, _, _, kind, _) | - not kind = ["summary", "source", "sink"] and - result = "Invalid kind \"" + kind + "\" in neutral model." - ) + predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _) } + + predicate neutralKind(string kind) { neutralModel(_, _, _, _, kind, _) } } + private module KindVal = SharedModelVal::KindValidation; + private string getInvalidModelSignature() { exists( string pred, string package, string type, string name, string signature, string ext, @@ -387,7 +319,7 @@ module ModelValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelKind() + KindVal::getInvalidModelKind() ] } } diff --git a/javascript/ql/lib/qlpack.yml b/javascript/ql/lib/qlpack.yml index 52962f549b09..d0efe4243168 100644 --- a/javascript/ql/lib/qlpack.yml +++ b/javascript/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: javascript library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fbb..2b765765e992 100644 --- a/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll +++ b/javascript/ql/lib/semmle/javascript/frameworks/data/internal/ApiGraphModels.qll @@ -653,6 +653,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; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +709,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() } } diff --git a/python/ql/lib/qlpack.yml b/python/ql/lib/qlpack.yml index 9d4522d5f589..0d2694d78fe2 100644 --- a/python/ql/lib/qlpack.yml +++ b/python/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: python library: true upgrades: upgrades dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace} diff --git a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fbb..2b765765e992 100644 --- a/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll +++ b/python/ql/lib/semmle/python/frameworks/data/internal/ApiGraphModels.qll @@ -653,6 +653,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; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +709,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() } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll index 227f4ea22fbb..2b765765e992 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModels.qll @@ -653,6 +653,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; /** * Gets an error message relating to an invalid CSV row in a model. @@ -698,5 +709,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() } } diff --git a/ruby/ql/lib/qlpack.yml b/ruby/ql/lib/qlpack.yml index bb01a5ff87d0..d201621268e6 100644 --- a/ruby/ql/lib/qlpack.yml +++ b/ruby/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ dbscheme: ruby.dbscheme upgrades: upgrades library: true dependencies: + codeql/mad: ${workspace} codeql/regex: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} diff --git a/shared/mad/codeql/mad/ModelValidation.qll b/shared/mad/codeql/mad/ModelValidation.qll new file mode 100644 index 000000000000..d5108c2eeec2 --- /dev/null +++ b/shared/mad/codeql/mad/ModelValidation.qll @@ -0,0 +1,186 @@ +/** + * Provides classes and predicates related to validating models-as-data rows. + */ + +/** Provides predicates for determining if a model exists for a given `kind`. */ +signature module KindValidationConfigSig { + /** Holds if a summary model exists for the given `kind`. */ + predicate summaryKind(string kind); + + /** Holds if a sink model exists for the given `kind`. */ + predicate sinkKind(string kind); + + /** Holds if a source model exists for the given `kind`. */ + predicate sourceKind(string kind); + + /** Holds if a neutral model exists for the given `kind`. */ + default predicate neutralKind(string kind) { none() } +} + +/** Provides validation for models-as-data summary, sink, source, and neutral kinds. */ +module KindValidation { + /** A valid models-as-data sink kind. */ + private class ValidSinkKind extends string { + bindingset[this] + ValidSinkKind() { + this = + [ + // shared + "code-injection", "command-injection", "file-content-store", "html-injection", + "js-injection", "ldap-injection", "log-injection", "path-injection", "request-forgery", + "sql-injection", "url-redirection", + // Java-only currently, but may be shared in the future + "bean-validation", "fragment-injection", "groovy-injection", "hostname-verification", + "information-leak", "intent-redirection", "jexl-injection", "jndi-injection", + "mvel-injection", "ognl-injection", "pending-intents", "response-splitting", + "template-injection", "xpath-injection", "xslt-injection", + // JavaScript-only currently, but may be shared in the future + "mongodb.sink", "nosql-injection", "unsafe-deserialization", + // Swift-only currently, but may be shared in the future + "database-store", "format-string", "hash-iteration-count", "predicate-injection", + "preferences-store", "tls-protocol-version", "transmission", "webview-fetch", "xxe" + ] + or + this.matches([ + // shared + "encryption-%", "qltest%", "test-%", + // Java-only currently, but may be shared in the future + "regex-use%", + // JavaScript-only currently, but may be shared in the future + "credentials-%", + // Swift-only currently, but may be shared in the future + "%string-%length", "weak-hash-input-%" + ]) + } + } + + /** An outdated models-as-data sink kind. */ + private class OutdatedSinkKind extends string { + OutdatedSinkKind() { + this = + [ + "sql", "url-redirect", "xpath", "ssti", "logging", "groovy", "jexl", "mvel", "xslt", + "ldap", "pending-intent-sent", "intent-start", "set-hostname-verifier", + "header-splitting", "xss", "write-file", "create-file", "read-file", "open-url", + "jdbc-url", "command-line-injection", "code", "html", "remote", + "uncontrolled-format-string", "js-eval" + ] + } + + /** Gets a replacement kind for an outdated sink kind. */ + private string replacementKind() { + this = ["sql", "xpath", "groovy", "jexl", "mvel", "xslt", "ldap", "code", "html"] and + result = this + "-injection" + or + this = "js-eval" and result = "code-injection" + or + this = "url-redirect" and result = "url-redirection" + or + this = "ssti" and result = "template-injection" + or + this = "logging" and result = "log-injection" + or + this = "pending-intent-sent" and result = "pending-intents" + or + this = "intent-start" and result = "intent-redirection" + or + this = "set-hostname-verifier" and result = "hostname-verification" + or + this = "header-splitting" and result = "response-splitting" + or + this = "xss" and result = "html-injection\" or \"js-injection" + or + this = ["write-file", "remote"] and result = "file-content-store" + or + this = ["create-file", "read-file"] and result = "path-injection" + or + this = ["open-url", "jdbc-url"] and result = "request-forgery" + or + this = "command-line-injection" and result = "command-injection" + or + this = "uncontrolled-format-string" and result = "format-string" + } + + /** Gets an error message for an outdated sink kind. */ + string outdatedMessage() { + result = + "The kind \"" + this + "\" is outdated. Use \"" + this.replacementKind() + "\" instead." + } + } + + /** A valid models-as-data source kind. */ + private class ValidSourceKind extends string { + bindingset[this] + ValidSourceKind() { + this = + [ + // shared + "local", "remote", + // Java + "android-external-storage-dir", "contentprovider", + // C# + "file", "file-write", + // JavaScript + "database-access-result" + ] + or + this.matches([ + // shared + "qltest%", "test-%", + // Swift + "%string-%length" + ]) + } + } + + /** A valid models-as-data summary kind. */ + private class ValidSummaryKind extends string { + ValidSummaryKind() { + this = + [ + // shared + "taint", "value", + // Dynamic languages + "type" + ] + } + } + + /** A valid models-as-data neutral kind. */ + private class ValidNeutralKind extends string { + ValidNeutralKind() { + this = + [ + // Java/C# currently + "sink", "source", "summary" + ] + } + } + + /** Gets an error message relating to an invalid kind in a model. */ + string getInvalidModelKind() { + exists(string kind | Config::summaryKind(kind) | + not kind instanceof ValidSummaryKind and + result = "Invalid kind \"" + kind + "\" in summary model." + ) + or + exists(string kind, string msg | Config::sinkKind(kind) | + not kind instanceof ValidSinkKind and + msg = "Invalid kind \"" + kind + "\" in sink model." and + // The part of this message that refers to outdated sink kinds can be deleted after June 1st, 2024. + if kind instanceof OutdatedSinkKind + then result = msg + " " + kind.(OutdatedSinkKind).outdatedMessage() + else result = msg + ) + or + exists(string kind | Config::sourceKind(kind) | + not kind instanceof ValidSourceKind and + result = "Invalid kind \"" + kind + "\" in source model." + ) + or + exists(string kind | Config::neutralKind(kind) | + not kind instanceof ValidNeutralKind and + result = "Invalid kind \"" + kind + "\" in neutral model." + ) + } +} diff --git a/shared/mad/qlpack.yml b/shared/mad/qlpack.yml new file mode 100644 index 000000000000..27e6c8e97504 --- /dev/null +++ b/shared/mad/qlpack.yml @@ -0,0 +1,6 @@ +name: codeql/mad +version: 0.0.1-dev +groups: shared +library: true +dependencies: +warnOnImplicitThis: true diff --git a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll index 82daf14a39a3..ba680b1b1382 100644 --- a/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll +++ b/swift/ql/lib/codeql/swift/dataflow/ExternalFlow.qll @@ -74,6 +74,7 @@ private import internal.FlowSummaryImpl::Public private import internal.FlowSummaryImpl::Private::External private import internal.FlowSummaryImplSpecific private import FlowSummary as FlowSummary +private import codeql.mad.ModelValidation as SharedModelVal /** * A unit class for adding additional source model rows. @@ -263,14 +264,16 @@ module CsvValidation { ) } - private string getInvalidModelKind() { - exists(string row, string kind | summaryModel(row) | - kind = row.splitAt(";", 8) and - not kind = ["taint", "value"] and - result = "Invalid kind \"" + kind + "\" in summary model." - ) + 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; + private string getInvalidModelSubtype() { exists(string pred, string row | sourceModel(row) and pred = "source" @@ -335,7 +338,7 @@ module CsvValidation { msg = [ getInvalidModelSignature(), getInvalidModelInput(), getInvalidModelOutput(), - getInvalidModelSubtype(), getInvalidModelColumnCount(), getInvalidModelKind() + getInvalidModelSubtype(), getInvalidModelColumnCount(), KindVal::getInvalidModelKind() ] } } diff --git a/swift/ql/lib/qlpack.yml b/swift/ql/lib/qlpack.yml index f45d347bad3c..f5335ce0f3f9 100644 --- a/swift/ql/lib/qlpack.yml +++ b/swift/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ dbscheme: swift.dbscheme upgrades: upgrades library: true dependencies: + codeql/mad: ${workspace} codeql/ssa: ${workspace} codeql/tutorial: ${workspace} codeql/util: ${workspace}