mirror of
https://github.com/github/codeql.git
synced 2026-02-12 05:01:06 +01:00
Merge branch 'github:main' into couchdb
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
## 7.8.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
## 7.8.1
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
3
java/ql/lib/change-notes/released/7.8.2.md
Normal file
3
java/ql/lib/change-notes/released/7.8.2.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 7.8.2
|
||||
|
||||
No user-facing changes.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 7.8.1
|
||||
lastReleaseVersion: 7.8.2
|
||||
|
||||
@@ -13,6 +13,14 @@ extensions:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data: []
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: neutralModel
|
||||
|
||||
@@ -50,6 +50,12 @@ extensions:
|
||||
- ["hudson", "FilePath", False, "readToString", "", "", "ReturnValue", "file", "manual"]
|
||||
- ["hudson", "Plugin", True, "configure", "", "", "Parameter", "remote", "manual"]
|
||||
- ["hudson", "Plugin", True, "newInstance", "", "", "Parameter", "remote", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["hudson", "Util", True, "escape", "(String)", "", "ReturnValue", "html-injection", "manual"]
|
||||
# Not including xmlEscape because it only accounts for >, <, and &. It does not account for ", or ', which makes it an incomplete XSS sanitizer.
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -162,3 +162,8 @@ extensions:
|
||||
extensible: sourceModel
|
||||
data:
|
||||
- ["java.io", "FileInputStream", True, "FileInputStream", "", "", "Argument[this]", "file", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["java.io", "File", True, "getName", "()", "", "ReturnValue", "path-injection", "manual"]
|
||||
|
||||
@@ -34,6 +34,11 @@ extensions:
|
||||
- ["java.net", "URLClassLoader", False, "URLClassLoader", "(URL[],ClassLoader)", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["java.net", "URLClassLoader", False, "URLClassLoader", "(URL[])", "", "Argument[0]", "request-forgery", "manual"]
|
||||
- ["java.net", "PasswordAuthentication", False, "PasswordAuthentication", "(String,char[])", "", "Argument[0]", "credentials-username", "hq-generated"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data:
|
||||
- ["java.net", "URI", True, "isAbsolute", "()", "", "Argument[this]", "false", "request-forgery", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -12,6 +12,11 @@ extensions:
|
||||
- ["java.util.regex", "Pattern", False, "split", "(CharSequence)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- ["java.util.regex", "Pattern", False, "split", "(CharSequence,int)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- ["java.util.regex", "Pattern", False, "splitAsStream", "(CharSequence)", "", "Argument[this]", "regex-use[0]", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["java.util.regex", "Pattern", False, "quote", "(String)", "", "ReturnValue", "regex-use", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
|
||||
@@ -1,6 +1,42 @@
|
||||
extensions:
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierGuardModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidCreditCard", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDate", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDirectoryPath", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidDouble", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidFileContent", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidFileName", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidInput", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidInteger", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidListItem", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidNumber", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidPrintable", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidRedirectLocation", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidSafeHTML", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "isValidURI", "", "", "Argument[1]", "true", "trust-boundary-violation", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: barrierModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidCreditCard", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDate", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDirectoryPath", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidDouble", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidFileContent", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidFileName", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidInput", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidInteger", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidListItem", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidNumber", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidPrintable", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidRedirectLocation", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidSafeHTML", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- ["org.owasp.esapi", "Validator", true, "getValidURI", "", "", "ReturnValue", "trust-boundary-violation", "manual"]
|
||||
- addsTo:
|
||||
pack: codeql/java-all
|
||||
extensible: summaryModel
|
||||
data:
|
||||
- ["org.owasp.esapi", "Encoder", true, "encodeForHTML", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
- ["org.owasp.esapi", "Encoder", true, "encodeForHTML", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 7.8.2-dev
|
||||
version: 7.8.3-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -91,14 +91,60 @@ module;
|
||||
|
||||
import java
|
||||
private import semmle.code.java.dataflow.DataFlow::DataFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import FlowSummary as FlowSummary
|
||||
private import internal.DataFlowPrivate
|
||||
private import internal.FlowSummaryImpl
|
||||
private import internal.FlowSummaryImpl::Public
|
||||
private import internal.FlowSummaryImpl::Private
|
||||
private import internal.FlowSummaryImpl::Private::External
|
||||
private import internal.ExternalFlowExtensions as Extensions
|
||||
private import internal.ExternalFlowExtensions::Extensions as Extensions
|
||||
private import codeql.mad.ModelValidation as SharedModelVal
|
||||
private import codeql.mad.static.ModelsAsData as SharedMaD
|
||||
|
||||
private module MadInput implements SharedMaD::InputSig {
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate additionalSourceModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate additionalSinkModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate additionalSummaryModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, string model
|
||||
) {
|
||||
exists(QlBuiltins::ExtensionId madId |
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId) and
|
||||
model = "MaD:" + madId.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private module MaD = SharedMaD::ModelsAsData<Extensions, MadInput>;
|
||||
|
||||
import MaD
|
||||
|
||||
/**
|
||||
* A class for activating additional model rows.
|
||||
@@ -146,60 +192,18 @@ abstract private class ActiveExperimentalModelsInternal extends string {
|
||||
|
||||
deprecated class ActiveExperimentalModels = ActiveExperimentalModelsInternal;
|
||||
|
||||
/** Holds if a source model exists for the given parameters. */
|
||||
predicate sourceModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance,
|
||||
madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a sink model exists for the given parameters. */
|
||||
predicate sinkModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance,
|
||||
madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a summary model exists for the given parameters. */
|
||||
predicate summaryModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
) {
|
||||
(
|
||||
Extensions::summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId)
|
||||
or
|
||||
any(ActiveExperimentalModelsInternal q)
|
||||
.summaryModel(package, type, subtypes, name, signature, ext, input, output, kind,
|
||||
provenance, madId)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the given extension tuple `madId` should pretty-print as `model`.
|
||||
*
|
||||
* This predicate should only be used in tests.
|
||||
*/
|
||||
predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
MaD::interpretModelForTest(madId, model)
|
||||
or
|
||||
exists(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance
|
||||
|
|
||||
sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance, madId) or
|
||||
Extensions::experimentalSourceModel(package, type, subtypes, name, signature, ext, output, kind,
|
||||
provenance, _, madId)
|
||||
|
|
||||
@@ -212,7 +216,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string kind, string provenance
|
||||
|
|
||||
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance, madId) or
|
||||
Extensions::experimentalSinkModel(package, type, subtypes, name, signature, ext, input, kind,
|
||||
provenance, _, madId)
|
||||
|
|
||||
@@ -225,8 +228,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string output, string kind, string provenance
|
||||
|
|
||||
summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
madId) or
|
||||
Extensions::experimentalSummaryModel(package, type, subtypes, name, signature, ext, input,
|
||||
output, kind, provenance, _, madId)
|
||||
|
|
||||
@@ -236,65 +237,6 @@ predicate interpretModelForTest(QlBuiltins::ExtensionId madId, string model) {
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if a neutral model exists for the given parameters. */
|
||||
predicate neutralModel = Extensions::neutralModel/6;
|
||||
|
||||
private predicate relevantPackage(string package) {
|
||||
sourceModel(package, _, _, _, _, _, _, _, _, _) or
|
||||
sinkModel(package, _, _, _, _, _, _, _, _, _) or
|
||||
summaryModel(package, _, _, _, _, _, _, _, _, _, _)
|
||||
}
|
||||
|
||||
private predicate packageLink(string shortpkg, string longpkg) {
|
||||
relevantPackage(shortpkg) and
|
||||
relevantPackage(longpkg) and
|
||||
longpkg.prefix(longpkg.indexOf(".")) = shortpkg
|
||||
}
|
||||
|
||||
private predicate canonicalPackage(string package) {
|
||||
relevantPackage(package) and not packageLink(_, package)
|
||||
}
|
||||
|
||||
private predicate canonicalPkgLink(string package, string subpkg) {
|
||||
canonicalPackage(package) and
|
||||
(subpkg = package or packageLink(package, subpkg))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if MaD framework coverage of `package` is `n` api endpoints of the
|
||||
* kind `(kind, part)`, and `pkgs` is the number of subpackages of `package`
|
||||
* which have MaD framework coverage (including `package` itself).
|
||||
*/
|
||||
predicate modelCoverage(string package, int pkgs, string kind, string part, int n) {
|
||||
pkgs = strictcount(string subpkg | canonicalPkgLink(package, subpkg)) and
|
||||
(
|
||||
part = "source" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string output, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
sourceModel(subpkg, type, subtypes, name, signature, ext, output, kind, provenance, _)
|
||||
)
|
||||
or
|
||||
part = "sink" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
sinkModel(subpkg, type, subtypes, name, signature, ext, input, kind, provenance, _)
|
||||
)
|
||||
or
|
||||
part = "summary" and
|
||||
n =
|
||||
strictcount(string subpkg, string type, boolean subtypes, string name, string signature,
|
||||
string ext, string input, string output, string provenance |
|
||||
canonicalPkgLink(package, subpkg) and
|
||||
summaryModel(subpkg, type, subtypes, name, signature, ext, input, output, kind, provenance,
|
||||
_)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Provides a query predicate to check the MaD models for validation errors. */
|
||||
module ModelValidation {
|
||||
private import codeql.dataflow.internal.AccessPathSyntax as AccessPathSyntax
|
||||
@@ -303,7 +245,9 @@ module ModelValidation {
|
||||
summaryModel(_, _, _, _, _, _, path, _, _, _, _) or
|
||||
summaryModel(_, _, _, _, _, _, _, path, _, _, _) or
|
||||
sinkModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _)
|
||||
sourceModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierModel(_, _, _, _, _, _, path, _, _, _) or
|
||||
barrierGuardModel(_, _, _, _, _, _, path, _, _, _, _)
|
||||
}
|
||||
|
||||
private module MkAccessPath = AccessPathSyntax::AccessPath<getRelevantAccessPath/1>;
|
||||
@@ -316,6 +260,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath input, AccessPathToken part |
|
||||
sinkModel(_, _, _, _, _, _, input, _, _, _) and pred = "sink"
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "barrier guard"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, input, _, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -338,6 +284,8 @@ module ModelValidation {
|
||||
exists(string pred, AccessPath output, AccessPathToken part |
|
||||
sourceModel(_, _, _, _, _, _, output, _, _, _) and pred = "source"
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, output, _, _, _) and pred = "barrier"
|
||||
or
|
||||
summaryModel(_, _, _, _, _, _, _, output, _, _, _) and pred = "summary"
|
||||
|
|
||||
(
|
||||
@@ -355,7 +303,13 @@ module ModelValidation {
|
||||
private module KindValConfig implements SharedModelVal::KindValidationConfigSig {
|
||||
predicate summaryKind(string kind) { summaryModel(_, _, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
predicate sinkKind(string kind) { sinkModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
predicate sinkKind(string kind) {
|
||||
sinkModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierModel(_, _, _, _, _, _, _, kind, _, _)
|
||||
or
|
||||
barrierGuardModel(_, _, _, _, _, _, _, _, kind, _, _)
|
||||
}
|
||||
|
||||
predicate sourceKind(string kind) { sourceModel(_, _, _, _, _, _, _, kind, _, _) }
|
||||
|
||||
@@ -373,6 +327,11 @@ module ModelValidation {
|
||||
or
|
||||
sinkModel(package, type, _, name, signature, ext, _, _, provenance, _) and pred = "sink"
|
||||
or
|
||||
barrierModel(package, type, _, name, signature, ext, _, _, provenance, _) and pred = "barrier"
|
||||
or
|
||||
barrierGuardModel(package, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "barrier guard"
|
||||
or
|
||||
summaryModel(package, type, _, name, signature, ext, _, _, _, provenance, _) and
|
||||
pred = "summary"
|
||||
or
|
||||
@@ -398,6 +357,14 @@ module ModelValidation {
|
||||
invalidProvenance(provenance) and
|
||||
result = "Unrecognized provenance description \"" + provenance + "\" in " + pred + " model."
|
||||
)
|
||||
or
|
||||
exists(string acceptingvalue |
|
||||
barrierGuardModel(_, _, _, _, _, _, _, acceptingvalue, _, _, _) and
|
||||
invalidAcceptingValue(acceptingvalue) and
|
||||
result =
|
||||
"Unrecognized accepting value description \"" + acceptingvalue +
|
||||
"\" in barrier guard model."
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if some row in a MaD flow model appears to contain typos. */
|
||||
@@ -418,6 +385,10 @@ private predicate elementSpec(
|
||||
or
|
||||
sinkModel(package, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierModel(package, type, subtypes, name, signature, ext, _, _, _, _)
|
||||
or
|
||||
barrierGuardModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
summaryModel(package, type, subtypes, name, signature, ext, _, _, _, _, _)
|
||||
or
|
||||
neutralModel(package, type, name, signature, _, _) and ext = "" and subtypes = true
|
||||
@@ -578,6 +549,53 @@ private module Cached {
|
||||
isSinkNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
}
|
||||
|
||||
private newtype TKindModelPair =
|
||||
TMkPair(string kind, string model) { isBarrierGuardNode(_, _, kind, model) }
|
||||
|
||||
private GuardValue convertAcceptingValue(AcceptingValue av) {
|
||||
av.isTrue() and result.asBooleanValue() = true
|
||||
or
|
||||
av.isFalse() and result.asBooleanValue() = false
|
||||
or
|
||||
av.isNoException() and result.getDualValue().isThrowsException()
|
||||
or
|
||||
av.isZero() and result.asIntValue() = 0
|
||||
or
|
||||
av.isNotZero() and result.getDualValue().asIntValue() = 0
|
||||
or
|
||||
av.isNull() and result.isNullValue()
|
||||
or
|
||||
av.isNotNull() and result.isNonNullValue()
|
||||
}
|
||||
|
||||
private predicate barrierGuardChecks(Guard g, Expr e, GuardValue gv, TKindModelPair kmp) {
|
||||
exists(
|
||||
SourceSinkInterpretationInput::InterpretNode n, AcceptingValue acceptingvalue, string kind,
|
||||
string model
|
||||
|
|
||||
isBarrierGuardNode(n, acceptingvalue, kind, model) and
|
||||
n.asNode().asExpr() = e and
|
||||
kmp = TMkPair(kind, model) and
|
||||
gv = convertAcceptingValue(acceptingvalue)
|
||||
|
|
||||
g.(Call).getAnArgument() = e or g.(MethodCall).getQualifier() = e
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
cached
|
||||
predicate barrierNode(Node node, string kind, string model) {
|
||||
exists(SourceSinkInterpretationInput::InterpretNode n |
|
||||
isBarrierNode(n, kind, model) and n.asNode() = node
|
||||
)
|
||||
or
|
||||
ParameterizedBarrierGuard<TKindModelPair, barrierGuardChecks/4>::getABarrierNode(TMkPair(kind,
|
||||
model)) = node
|
||||
}
|
||||
}
|
||||
|
||||
import Cached
|
||||
@@ -594,6 +612,12 @@ predicate sourceNode(Node node, string kind) { sourceNode(node, kind, _) }
|
||||
*/
|
||||
predicate sinkNode(Node node, string kind) { sinkNode(node, kind, _) }
|
||||
|
||||
/**
|
||||
* Holds if `node` is specified as a barrier with the given kind in a MaD flow
|
||||
* model.
|
||||
*/
|
||||
predicate barrierNode(Node node, string kind) { barrierNode(node, kind, _) }
|
||||
|
||||
// adapter class for converting Mad summaries to `SummarizedCallable`s
|
||||
private class SummarizedCallableAdapter extends SummarizedCallable {
|
||||
SummarizedCallableAdapter() { summaryElement(this, _, _, _, _, _, _) }
|
||||
|
||||
@@ -60,7 +60,7 @@ private module DispatchImpl {
|
||||
not (
|
||||
// Only use summarized callables with generated summaries in case
|
||||
// the static call target is not in the source code.
|
||||
// Note that if applyGeneratedModel holds it implies that there doesn't
|
||||
// Note that if `applyGeneratedModel` holds it implies that there doesn't
|
||||
// exist a manual model.
|
||||
exists(Callable staticTarget | staticTarget = call.getCallee().getSourceDeclaration() |
|
||||
staticTarget.fromSource() and not staticTarget.isStub()
|
||||
|
||||
@@ -374,6 +374,29 @@ class ContentSet instanceof Content {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `gv`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate valueGuardChecksSig(Guard g, Expr e, GuardValue gv);
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuardValue<valueGuardChecksSig/3 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
SsaFlow::asNode(result) =
|
||||
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `branch`.
|
||||
*
|
||||
@@ -390,9 +413,38 @@ signature predicate guardChecksSig(Guard g, Expr e, boolean branch);
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
private predicate guardChecks0(Guard g, Expr e, GuardValue gv) {
|
||||
guardChecks(g, e, gv.asBooleanValue())
|
||||
}
|
||||
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode() {
|
||||
Node getABarrierNode() { result = BarrierGuardValue<guardChecks0/3>::getABarrierNode() }
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
/**
|
||||
* Holds if the guard `g` validates the expression `e` upon evaluating to `gv`.
|
||||
*
|
||||
* The expression `e` is expected to be a syntactic part of the guard `g`.
|
||||
* For example, the guard `g` might be a call `isSafe(x)` and the expression `e`
|
||||
* the argument `x`.
|
||||
*/
|
||||
signature predicate guardChecksSig(Guard g, Expr e, GuardValue gv, P param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a set of barrier nodes for a guard that validates an expression.
|
||||
*
|
||||
* This is expected to be used in `isBarrier`/`isSanitizer` definitions
|
||||
* in data flow and taint tracking.
|
||||
*/
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
/** Gets a node that is safely guarded by the given guard check. */
|
||||
Node getABarrierNode(P param) {
|
||||
SsaFlow::asNode(result) =
|
||||
SsaImpl::DataFlowIntegration::BarrierGuard<guardChecks/3>::getABarrierNode()
|
||||
SsaImpl::DataFlowIntegration::ParameterizedBarrierGuard<P, guardChecks/4>::getABarrierNode(param)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
private import codeql.mad.static.ModelsAsData as SharedMaD
|
||||
|
||||
/**
|
||||
* Holds if a source model exists for the given parameters.
|
||||
*/
|
||||
@@ -20,6 +22,22 @@ extensible predicate sinkModel(
|
||||
string input, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a barrier model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate barrierModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string output, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a barrier guard model exists for the given parameters.
|
||||
*/
|
||||
extensible predicate barrierGuardModel(
|
||||
string package, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string input, string acceptingvalue, string kind, string provenance, QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
/**
|
||||
* Holds if a summary model exists for the given parameters.
|
||||
*/
|
||||
@@ -77,3 +95,9 @@ extensible predicate experimentalSummaryModel(
|
||||
string input, string output, string kind, string provenance, string filter,
|
||||
QlBuiltins::ExtensionId madId
|
||||
);
|
||||
|
||||
module Extensions implements SharedMaD::ExtensionsSig {
|
||||
import ExternalFlowExtensions
|
||||
|
||||
predicate namespaceGrouping(string group, string namespace) { none() }
|
||||
}
|
||||
|
||||
@@ -158,7 +158,9 @@ private predicate relatedArgSpec(Callable c, string spec) {
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _, _) or
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, _, spec, _, _, _) or
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _)
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _) or
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, spec, _, _, _, _)
|
||||
|
|
||||
c = interpretElement(namespace, type, subtypes, name, signature, ext, _)
|
||||
)
|
||||
@@ -226,11 +228,10 @@ module SourceSinkInterpretationInput implements
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSource, string originalOutput, QlBuiltins::ExtensionId madId
|
||||
SourceOrSinkElement baseSource, string originalOutput
|
||||
|
|
||||
sourceModel(namespace, type, subtypes, name, signature, ext, originalOutput, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
model) and
|
||||
baseSource = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseSource and output = originalOutput
|
||||
@@ -245,11 +246,10 @@ module SourceSinkInterpretationInput implements
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseSink, string originalInput, QlBuiltins::ExtensionId madId
|
||||
SourceOrSinkElement baseSink, string originalInput
|
||||
|
|
||||
sinkModel(namespace, type, subtypes, name, signature, ext, originalInput, kind, provenance,
|
||||
madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
model) and
|
||||
baseSink = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseSink and originalInput = input
|
||||
@@ -259,6 +259,43 @@ module SourceSinkInterpretationInput implements
|
||||
)
|
||||
}
|
||||
|
||||
predicate barrierElement(
|
||||
Element e, string output, string kind, Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseBarrier, string originalOutput
|
||||
|
|
||||
barrierModel(namespace, type, subtypes, name, signature, ext, originalOutput, kind,
|
||||
provenance, model) and
|
||||
baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseBarrier and output = originalOutput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseBarrier, e, originalOutput, output)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
predicate barrierGuardElement(
|
||||
Element e, string input, Public::AcceptingValue acceptingvalue, string kind,
|
||||
Public::Provenance provenance, string model
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
SourceOrSinkElement baseBarrier, string originalInput
|
||||
|
|
||||
barrierGuardModel(namespace, type, subtypes, name, signature, ext, originalInput,
|
||||
acceptingvalue, kind, provenance, model) and
|
||||
baseBarrier = interpretElement(namespace, type, subtypes, name, signature, ext, _) and
|
||||
(
|
||||
e = baseBarrier and input = originalInput
|
||||
or
|
||||
correspondingKotlinParameterDefaultsArgSpec(baseBarrier, e, originalInput, input)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
class SourceOrSinkElement = Element;
|
||||
|
||||
private newtype TInterpretNode =
|
||||
@@ -343,12 +380,10 @@ module Private {
|
||||
) {
|
||||
exists(
|
||||
string namespace, string type, boolean subtypes, string name, string signature, string ext,
|
||||
string originalInput, string originalOutput, Callable baseCallable,
|
||||
QlBuiltins::ExtensionId madId
|
||||
string originalInput, string originalOutput, Callable baseCallable
|
||||
|
|
||||
summaryModel(namespace, type, subtypes, name, signature, ext, originalInput, originalOutput,
|
||||
kind, provenance, madId) and
|
||||
model = "MaD:" + madId.toString() and
|
||||
kind, provenance, model) and
|
||||
baseCallable = interpretElement(namespace, type, subtypes, name, signature, ext, isExact) and
|
||||
(
|
||||
c.asCallable() = baseCallable and input = originalInput and output = originalOutput
|
||||
|
||||
@@ -564,12 +564,14 @@ private module Cached {
|
||||
DataFlowIntegrationImpl::localMustFlowStep(v, nodeFrom, nodeTo)
|
||||
}
|
||||
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, boolean branch);
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::GuardValue gv);
|
||||
|
||||
cached // nothing is actually cached
|
||||
module BarrierGuard<guardChecksSig/3 guardChecks> {
|
||||
private predicate guardChecksAdjTypes(Guards::Guards_v3::Guard g, Expr e, boolean branch) {
|
||||
guardChecks(g, e, branch)
|
||||
private predicate guardChecksAdjTypes(
|
||||
Guards::Guards_v3::Guard g, Expr e, Guards::GuardValue gv
|
||||
) {
|
||||
guardChecks(g, e, gv)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
@@ -586,6 +588,36 @@ private module Cached {
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/0;
|
||||
}
|
||||
|
||||
bindingset[this]
|
||||
private signature class ParamSig;
|
||||
|
||||
private module WithParam<ParamSig P> {
|
||||
signature predicate guardChecksSig(Guards::Guard g, Expr e, Guards::GuardValue gv, P param);
|
||||
}
|
||||
|
||||
cached // nothing is actually cached
|
||||
module ParameterizedBarrierGuard<ParamSig P, WithParam<P>::guardChecksSig/4 guardChecks> {
|
||||
private predicate guardChecksAdjTypes(
|
||||
Guards::Guards_v3::Guard g, Expr e, Guards::GuardValue gv, P param
|
||||
) {
|
||||
guardChecks(g, e, gv, param)
|
||||
}
|
||||
|
||||
private predicate guardChecksWithWrappers(
|
||||
DataFlowIntegrationInput::Guard g, Definition def, Guards::GuardValue val, P param
|
||||
) {
|
||||
Guards::Guards_v3::ParameterizedValidationWrapper<P, guardChecksAdjTypes/4>::guardChecksDef(g,
|
||||
def, val, param)
|
||||
}
|
||||
|
||||
private Node getABarrierNodeImpl(P param) {
|
||||
result =
|
||||
DataFlowIntegrationImpl::BarrierGuardDefWithState<P, guardChecksWithWrappers/4>::getABarrierNode(param)
|
||||
}
|
||||
|
||||
predicate getABarrierNode = getABarrierNodeImpl/1;
|
||||
}
|
||||
}
|
||||
|
||||
cached
|
||||
|
||||
@@ -14,14 +14,3 @@ class HudsonWebMethod extends Method {
|
||||
this.getDeclaringType().getASourceSupertype*().hasQualifiedName("hudson.model", "Descriptor")
|
||||
}
|
||||
}
|
||||
|
||||
private class HudsonUtilXssSanitizer extends XssSanitizer {
|
||||
HudsonUtilXssSanitizer() {
|
||||
this.asExpr()
|
||||
.(MethodCall)
|
||||
.getMethod()
|
||||
// Not including xmlEscape because it only accounts for >, <, and &.
|
||||
// It does not account for ", or ', which makes it an incomplete XSS sanitizer.
|
||||
.hasQualifiedName("hudson", "Util", "escape")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/** Classes and predicates for reasoning about the `owasp.easpi` package. */
|
||||
overlay[local?]
|
||||
module;
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* The `org.owasp.esapi.Validator` interface.
|
||||
*/
|
||||
class EsapiValidator extends RefType {
|
||||
EsapiValidator() { this.hasQualifiedName("org.owasp.esapi", "Validator") }
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods of `org.owasp.esapi.Validator` which validate data.
|
||||
*/
|
||||
class EsapiIsValidMethod extends Method {
|
||||
EsapiIsValidMethod() {
|
||||
this.getDeclaringType() instanceof EsapiValidator and
|
||||
this.hasName([
|
||||
"isValidCreditCard", "isValidDate", "isValidDirectoryPath", "isValidDouble",
|
||||
"isValidFileContent", "isValidFileName", "isValidInput", "isValidInteger",
|
||||
"isValidListItem", "isValidNumber", "isValidPrintable", "isValidRedirectLocation",
|
||||
"isValidSafeHTML", "isValidURI"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods of `org.owasp.esapi.Validator` which return validated data.
|
||||
*/
|
||||
class EsapiGetValidMethod extends Method {
|
||||
EsapiGetValidMethod() {
|
||||
this.getDeclaringType() instanceof EsapiValidator and
|
||||
this.hasName([
|
||||
"getValidCreditCard", "getValidDate", "getValidDirectoryPath", "getValidDouble",
|
||||
"getValidFileContent", "getValidFileName", "getValidInput", "getValidInteger",
|
||||
"getValidListItem", "getValidNumber", "getValidPrintable", "getValidRedirectLocation",
|
||||
"getValidSafeHTML", "getValidURI"
|
||||
])
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ module;
|
||||
|
||||
import java
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.dataflow.SSA
|
||||
private import semmle.code.java.frameworks.kotlin.IO
|
||||
@@ -288,19 +289,8 @@ private Method getSourceMethod(Method m) {
|
||||
result = m
|
||||
}
|
||||
|
||||
/**
|
||||
* A sanitizer that protects against path injection vulnerabilities
|
||||
* by extracting the final component of the user provided path.
|
||||
*
|
||||
* TODO: convert this class to models-as-data if sanitizer support is added
|
||||
*/
|
||||
private class FileGetNameSanitizer extends PathInjectionSanitizer {
|
||||
FileGetNameSanitizer() {
|
||||
exists(MethodCall mc |
|
||||
mc.getMethod().hasQualifiedName("java.io", "File", "getName") and
|
||||
this.asExpr() = mc
|
||||
)
|
||||
}
|
||||
private class DefaultPathInjectionSanitizer extends PathInjectionSanitizer {
|
||||
DefaultPathInjectionSanitizer() { barrierNode(this, "path-injection") }
|
||||
}
|
||||
|
||||
/** Holds if `g` is a guard that checks for `..` components. */
|
||||
|
||||
@@ -118,25 +118,8 @@ private class ContainsUrlSanitizer extends RequestForgerySanitizer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A check that the URL is relative, and therefore safe for URL redirects.
|
||||
*/
|
||||
private predicate isRelativeUrlSanitizer(Guard guard, Expr e, boolean branch) {
|
||||
guard =
|
||||
any(MethodCall call |
|
||||
call.getMethod().hasQualifiedName("java.net", "URI", "isAbsolute") and
|
||||
e = call.getQualifier() and
|
||||
branch = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A check that the URL is relative, and therefore safe for URL redirects.
|
||||
*/
|
||||
private class RelativeUrlSanitizer extends RequestForgerySanitizer {
|
||||
RelativeUrlSanitizer() {
|
||||
this = DataFlow::BarrierGuard<isRelativeUrlSanitizer/3>::getABarrierNode()
|
||||
}
|
||||
private class DefaultRequestForgerySanitizer extends RequestForgerySanitizer {
|
||||
DefaultRequestForgerySanitizer() { barrierNode(this, "request-forgery") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,6 @@ private import semmle.code.java.dataflow.DataFlow
|
||||
private import semmle.code.java.controlflow.Guards
|
||||
private import semmle.code.java.dataflow.ExternalFlow
|
||||
private import semmle.code.java.dataflow.FlowSources
|
||||
private import semmle.code.java.frameworks.owasp.Esapi
|
||||
private import semmle.code.java.security.Sanitizers
|
||||
|
||||
/**
|
||||
@@ -28,25 +27,8 @@ class TrustBoundaryViolationSink extends DataFlow::Node {
|
||||
*/
|
||||
abstract class TrustBoundaryValidationSanitizer extends DataFlow::Node { }
|
||||
|
||||
/**
|
||||
* A node validated by an OWASP ESAPI validation method.
|
||||
*/
|
||||
private class EsapiValidatedInputSanitizer extends TrustBoundaryValidationSanitizer {
|
||||
EsapiValidatedInputSanitizer() {
|
||||
this = DataFlow::BarrierGuard<esapiIsValidData/3>::getABarrierNode() or
|
||||
this.asExpr().(MethodCall).getMethod() instanceof EsapiGetValidMethod
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `g` is a guard that checks that `e` is valid data according to an OWASP ESAPI validation method.
|
||||
*/
|
||||
private predicate esapiIsValidData(Guard g, Expr e, boolean branch) {
|
||||
branch = true and
|
||||
exists(MethodCall ma | ma.getMethod() instanceof EsapiIsValidMethod |
|
||||
g = ma and
|
||||
e = ma.getArgument(1)
|
||||
)
|
||||
private class DefaultTrustBoundaryValidationSanitizer extends TrustBoundaryValidationSanitizer {
|
||||
DefaultTrustBoundaryValidationSanitizer() { barrierNode(this, "trust-boundary-violation") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,12 +54,24 @@ private class DefaultXssSink extends XssSink {
|
||||
}
|
||||
}
|
||||
|
||||
/** A default sanitizer that considers numeric and boolean typed data safe for writing to output. */
|
||||
private class DefaultXssSanitizer extends XssSanitizer {
|
||||
DefaultXssSanitizer() {
|
||||
DefaultXssSanitizer() { barrierNode(this, ["html-injection", "js-injection"]) }
|
||||
}
|
||||
|
||||
/** A sanitizer that considers numeric and boolean typed data safe for writing to output. */
|
||||
private class PrimitiveSanitizer extends XssSanitizer {
|
||||
PrimitiveSanitizer() {
|
||||
this.getType() instanceof NumericType or
|
||||
this.getType() instanceof BooleanType or
|
||||
// Match `org.springframework.web.util.HtmlUtils.htmlEscape` and possibly other methods like it.
|
||||
this.getType() instanceof BooleanType
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to `org.springframework.web.util.HtmlUtils.htmlEscape`, or possibly
|
||||
* other methods like it, considered as a sanitizer for XSS.
|
||||
*/
|
||||
private class HtmlEscapeXssSanitizer extends XssSanitizer {
|
||||
HtmlEscapeXssSanitizer() {
|
||||
this.asExpr().(MethodCall).getMethod().getName().regexpMatch("(?i)html_?escape.*")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,17 +21,8 @@ private class DefaultRegexInjectionSink extends RegexInjectionSink {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to the `Pattern.quote` method, which gives metacharacters or escape sequences
|
||||
* no special meaning.
|
||||
*/
|
||||
private class PatternQuoteCall extends RegexInjectionSanitizer {
|
||||
PatternQuoteCall() {
|
||||
exists(MethodCall ma, Method m | m = ma.getMethod() |
|
||||
ma.getArgument(0) = this.asExpr() and
|
||||
m instanceof PatternQuoteMethod
|
||||
)
|
||||
}
|
||||
private class DefaultRegexInjectionSanitizer extends RegexInjectionSanitizer {
|
||||
DefaultRegexInjectionSanitizer() { barrierNode(this, "regex-use") }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
## 1.10.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Java analysis no longer forces `--source` and `--target` compiler flags for Maven builds. This allows Maven to use the project's own compiler configuration, improving build compatibility.
|
||||
|
||||
## 1.10.2
|
||||
|
||||
No user-facing changes.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
## 1.10.3
|
||||
|
||||
### Minor Analysis Improvements
|
||||
|
||||
* Java analysis no longer forces `--source` and `--target` compiler flags for Maven builds. This allows Maven to use the project's own compiler configuration, improving build compatibility.
|
||||
@@ -1,2 +1,2 @@
|
||||
---
|
||||
lastReleaseVersion: 1.10.2
|
||||
lastReleaseVersion: 1.10.3
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-queries
|
||||
version: 1.10.3-dev
|
||||
version: 1.10.4-dev
|
||||
groups:
|
||||
- java
|
||||
- queries
|
||||
|
||||
@@ -4,7 +4,13 @@ public class A {
|
||||
|
||||
boolean isSafe(Object o) { return o == null; }
|
||||
|
||||
void foo() {
|
||||
void assertSafe(Object o) { if (o != null) throw new RuntimeException(); }
|
||||
|
||||
private boolean wrapIsSafe(Object o) { return isSafe(o); }
|
||||
|
||||
private void wrapAssertSafe(Object o) { assertSafe(o); }
|
||||
|
||||
void test1() {
|
||||
Object x = source();
|
||||
if (!isSafe(x)) {
|
||||
x = null;
|
||||
@@ -21,4 +27,23 @@ public class A {
|
||||
}
|
||||
sink(x);
|
||||
}
|
||||
|
||||
void test2() {
|
||||
Object x = source();
|
||||
assertSafe(x);
|
||||
sink(x);
|
||||
}
|
||||
|
||||
void test3() {
|
||||
Object x = source();
|
||||
if (wrapIsSafe(x)) {
|
||||
sink(x);
|
||||
}
|
||||
}
|
||||
|
||||
void test4() {
|
||||
Object x = source();
|
||||
wrapAssertSafe(x);
|
||||
sink(x);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@ private predicate isSafe(Guard g, Expr checked, boolean branch) {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate assertSafe(Guard g, Expr checked, GuardValue gv) {
|
||||
exists(MethodCall mc | g = mc |
|
||||
mc.getMethod().hasName("assertSafe") and
|
||||
checked = mc.getAnArgument() and
|
||||
gv.getDualValue().isThrowsException()
|
||||
)
|
||||
}
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr().(MethodCall).getMethod().hasName("source")
|
||||
@@ -21,6 +29,8 @@ module TestConfig implements DataFlow::ConfigSig {
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::BarrierGuard<isSafe/3>::getABarrierNode()
|
||||
or
|
||||
node = DataFlow::BarrierGuardValue<assertSafe/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user