- ^_(__|[^_])+_$
-
+^_(__|[^_])+_$
diff --git a/javascript/ql/src/Performance/ReDoS.qhelp b/javascript/ql/src/Performance/ReDoS.qhelp
index 21b937b17ef..a020a319207 100644
--- a/javascript/ql/src/Performance/ReDoS.qhelp
+++ b/javascript/ql/src/Performance/ReDoS.qhelp
@@ -25,8 +25,7 @@
the two branches of the alternative inside the repetition:
- /^_(__|[^_])+_$/
-
+/^_(__|[^_])+_$/
diff --git a/python/ql/src/Security/CWE-730/ReDoS.qhelp b/python/ql/src/Security/CWE-730/ReDoS.qhelp
index 9cfbcc32354..74f3b8b87a1 100644
--- a/python/ql/src/Security/CWE-730/ReDoS.qhelp
+++ b/python/ql/src/Security/CWE-730/ReDoS.qhelp
@@ -25,8 +25,7 @@
the two branches of the alternative inside the repetition:
- ^_(__|[^_])+_$
-
+^_(__|[^_])+_$
diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
index 4c85702a0d3..901315cba72 100644
--- a/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
+++ b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
@@ -21,8 +21,7 @@
repetition:
- /^_(__|[^_])+_$/
-
+/^_(__|[^_])+_$/
From 9f5bf8fb2268d6975e9d791ad1b54da89428646c Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Thu, 25 May 2023 13:56:29 +0200
Subject: [PATCH 093/813] also fix the first code-block
---
java/ql/src/Security/CWE/CWE-730/ReDoS.qhelp | 3 +--
javascript/ql/src/Performance/ReDoS.qhelp | 3 +--
python/ql/src/Security/CWE-730/ReDoS.qhelp | 3 +--
ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp | 3 +--
4 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/java/ql/src/Security/CWE/CWE-730/ReDoS.qhelp b/java/ql/src/Security/CWE/CWE-730/ReDoS.qhelp
index 9f0d7a6fa07..7fcdb97535b 100644
--- a/java/ql/src/Security/CWE/CWE-730/ReDoS.qhelp
+++ b/java/ql/src/Security/CWE/CWE-730/ReDoS.qhelp
@@ -11,8 +11,7 @@
Consider this regular expression:
- ^_(__|.)+_$
-
+^_(__|.)+_$
Its sub-expression "(__|.)+?" can match the string "__" either by the
first alternative "__" to the left of the "|" operator, or by two
diff --git a/javascript/ql/src/Performance/ReDoS.qhelp b/javascript/ql/src/Performance/ReDoS.qhelp
index a020a319207..c152d646201 100644
--- a/javascript/ql/src/Performance/ReDoS.qhelp
+++ b/javascript/ql/src/Performance/ReDoS.qhelp
@@ -11,8 +11,7 @@
Consider this regular expression:
- /^_(__|.)+_$/
-
+/^_(__|.)+_$/
Its sub-expression "(__|.)+?" can match the string "__" either by the
first alternative "__" to the left of the "|" operator, or by two
diff --git a/python/ql/src/Security/CWE-730/ReDoS.qhelp b/python/ql/src/Security/CWE-730/ReDoS.qhelp
index 74f3b8b87a1..a881d94cd9f 100644
--- a/python/ql/src/Security/CWE-730/ReDoS.qhelp
+++ b/python/ql/src/Security/CWE-730/ReDoS.qhelp
@@ -11,8 +11,7 @@
Consider this regular expression:
- ^_(__|.)+_$
-
+^_(__|.)+_$
Its sub-expression "(__|.)+?" can match the string "__" either by the
first alternative "__" to the left of the "|" operator, or by two
diff --git a/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
index 901315cba72..4c19e2bb6fe 100644
--- a/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
+++ b/ruby/ql/src/queries/security/cwe-1333/ReDoS.qhelp
@@ -4,8 +4,7 @@
Consider this regular expression:
- /^_(__|.)+_$/
-
+/^_(__|.)+_$/
Its sub-expression "(__|.)+?" can match the string
"__" either by the first alternative "__" to the
From 6fc16574b33c0f284a40d185a4dbd9125a35b8a5 Mon Sep 17 00:00:00 2001
From: Taus
Date: Mon, 22 May 2023 12:36:42 +0000
Subject: [PATCH 094/813] Java: Add QL support for automodel application mode
---
...utomodelApplicationModeCharacteristics.qll | 360 ++++++++++++++++++
...tomodelApplicationModeExtractCandidates.ql | 48 +++
...AutomodelFrameworkModeExtractCandidates.ql | 2 +-
3 files changed, 409 insertions(+), 1 deletion(-)
create mode 100644 java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
create mode 100644 java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
new file mode 100644
index 00000000000..b0dca8018a9
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -0,0 +1,360 @@
+/**
+ * For internal use only.
+ */
+
+private import java
+private import semmle.code.Location as Location
+private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.dataflow.TaintTracking
+private import semmle.code.java.security.PathCreation
+private import semmle.code.java.dataflow.ExternalFlow as ExternalFlow
+private import semmle.code.java.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl
+private import semmle.code.java.security.ExternalAPIs as ExternalAPIs
+private import semmle.code.java.Expr as Expr
+private import semmle.code.java.security.QueryInjection
+private import semmle.code.java.security.RequestForgery
+private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
+import AutomodelSharedCharacteristics as SharedCharacteristics
+import AutomodelEndpointTypes as AutomodelEndpointTypes
+
+/**
+ * A meta data extractor. Any Java extraction mode needs to implement exactly
+ * one instance of this class.
+ */
+abstract class MetadataExtractor extends string {
+ bindingset[this]
+ MetadataExtractor() { any() }
+
+ abstract predicate hasMetadata(
+ Endpoint e, string package, string type, boolean subtypes, string name, string signature,
+ int input
+ );
+}
+
+newtype JavaRelatedLocationType = CallContext()
+
+/**
+ * A class representing nodes that are arguments to calls.
+ */
+private class ArgumentNode extends DataFlow::Node {
+ ArgumentNode() { this.asExpr() = [any(Call c).getAnArgument(), any(Call c).getQualifier()] }
+}
+
+/**
+ * A candidates implementation for framework mode.
+ *
+ * Some important notes:
+ * - This mode is using parameters as endpoints.
+ * - Sink- and neutral-information is being used from MaD models.
+ * - When available, we use method- and class-java-docs as related locations.
+ */
+module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
+ // for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
+ class Endpoint = ArgumentNode;
+
+ class EndpointType = AutomodelEndpointTypes::EndpointType;
+
+ class NegativeEndpointType = AutomodelEndpointTypes::NegativeSinkType;
+
+ class RelatedLocation = Location::Top;
+
+ class RelatedLocationType = JavaRelatedLocationType;
+
+ // Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
+ predicate isSanitizer(Endpoint e, EndpointType t) { none() }
+
+ RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
+
+ predicate isKnownKind(string kind, string humanReadableKind, EndpointType type) {
+ kind = "read-file" and
+ humanReadableKind = "read file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ kind = "create-file" and
+ humanReadableKind = "create file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ kind = "sql" and
+ humanReadableKind = "mad modeled sql" and
+ type instanceof AutomodelEndpointTypes::SqlSinkType
+ or
+ kind = "open-url" and
+ humanReadableKind = "open url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ kind = "jdbc-url" and
+ humanReadableKind = "jdbc url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ kind = "command-injection" and
+ humanReadableKind = "command injection" and
+ type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
+ }
+
+ predicate isSink(Endpoint e, string kind) {
+ exists(string package, string type, string name, string signature, string ext, string input |
+ sinkSpec(e, package, type, name, signature, ext, input) and
+ ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, _)
+ )
+ }
+
+ predicate isNeutral(Endpoint e) {
+ exists(string package, string type, string name, string signature |
+ sinkSpec(e, package, type, name, signature, _, _) and
+ ExternalFlow::neutralModel(package, type, name, [signature, ""], _, _)
+ )
+ }
+
+ additional predicate sinkSpec(
+ Endpoint e, string package, string type, string name, string signature, string ext, string input
+ ) {
+ FrameworkCandidatesImpl::getCallable(e).hasQualifiedName(package, type, name) and
+ signature = ExternalFlow::paramsString(getCallable(e)) and
+ ext = "" and
+ (
+ exists(Call c, int argIdx |
+ e.asExpr() = c.getArgument(argIdx) and
+ input = "Argument[" + argIdx + "]"
+ )
+ or
+ exists(Call c | e.asExpr() = c.getQualifier() and input = "Argument[this]")
+ )
+ // exists(int paramIdx | e.isParameterOf(_, paramIdx) |
+ // if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]"
+ // )
+ }
+
+ /**
+ * Returns the related location for the given endpoint.
+ *
+ * Related locations can be JavaDoc comments of the class or the method.
+ */
+ RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
+ type = CallContext() and
+ result = asLocation(e)
+ }
+
+ /**
+ * Returns the callable that contains the given endpoint.
+ *
+ * Each Java mode should implement this predicate.
+ */
+ additional Callable getCallable(Endpoint e) {
+ exists(Call c |
+ e.asExpr() = [c.getAnArgument(), c.getQualifier()] and
+ result = c.getCallee()
+ )
+ }
+}
+
+module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics;
+
+class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
+
+class Endpoint = FrameworkCandidatesImpl::Endpoint;
+
+/*
+ * Predicates that are used to surface prompt examples and candidates for classification with an ML model.
+ */
+
+/**
+ * A MetadataExtractor that extracts metadata for framework mode.
+ */
+class FrameworkModeMetadataExtractor extends MetadataExtractor {
+ FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
+
+ /**
+ * By convention, the subtypes property of the MaD declaration should only be
+ * true when there _can_ exist any subtypes with a different implementation.
+ *
+ * It would technically be ok to always use the value 'true', but this would
+ * break convention.
+ */
+ boolean considerSubtypes(Callable callable) {
+ if
+ callable.isStatic() or
+ callable.getDeclaringType().isStatic() or
+ callable.isFinal() or
+ callable.getDeclaringType().isFinal()
+ then result = false
+ else result = true
+ }
+
+ override predicate hasMetadata(
+ Endpoint e, string package, string type, boolean subtypes, string name, string signature,
+ int input
+ ) {
+ exists(Call call, Callable callable |
+ call.getCallee() = callable and
+ (
+ e.asExpr() = call.getArgument(input)
+ or
+ e.asExpr() = call.getQualifier() and input = -1
+ ) and
+ package = callable.getDeclaringType().getPackage().getName() and
+ type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
+ subtypes = this.considerSubtypes(callable) and
+ name = callable.getName() and
+ signature = ExternalFlow::paramsString(callable)
+ )
+ }
+}
+
+/*
+ * EndpointCharacteristic classes that are specific to Automodel for Java.
+ */
+
+/**
+ * A negative characteristic that indicates that an is-style boolean method is unexploitable even if it is a sink.
+ *
+ * A sink is highly unlikely to be exploitable if its callable's name starts with `is` and the callable has a boolean return
+ * type (e.g. `isDirectory`). These kinds of calls normally do only checks, and appear before the proper call that does
+ * the dangerous/interesting thing, so we want the latter to be modeled as the sink.
+ *
+ * TODO: this might filter too much, it's possible that methods with more than one parameter contain interesting sinks
+ */
+private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not FrameworkCandidatesImpl::isSink(e, _) and
+ FrameworkCandidatesImpl::getCallable(e).getName().matches("is%") and
+ FrameworkCandidatesImpl::getCallable(e).getReturnType() instanceof BooleanType
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an existence-checking boolean method is unexploitable even if it is a
+ * sink.
+ *
+ * A sink is highly unlikely to be exploitable if its callable's name is `exists` or `notExists` and the callable has a
+ * boolean return type. These kinds of calls normally do only checks, and appear before the proper call that does the
+ * dangerous/interesting thing, so we want the latter to be modeled as the sink.
+ */
+private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not FrameworkCandidatesImpl::isSink(e, _) and
+ exists(Callable callable |
+ callable = FrameworkCandidatesImpl::getCallable(e) and
+ callable.getName().toLowerCase() = ["exists", "notexists"] and
+ callable.getReturnType() instanceof BooleanType
+ )
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an endpoint is an argument to an exception, which is not a sink.
+ */
+private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ ExceptionCharacteristic() { this = "exception" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ FrameworkCandidatesImpl::getCallable(e).getDeclaringType().getASupertype*() instanceof
+ TypeThrowable
+ }
+}
+
+/**
+ * A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
+ * are considered worth modeling.
+ */
+private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingToModelCharacteristic {
+ NotAModelApiParameter() { this = "not a model API parameter" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not exists(ModelExclusions::ModelApi api |
+ exists(Call c |
+ c.getCallee() = api and
+ exists(int argIdx | exists(api.getParameter(argIdx)) |
+ argIdx = -1 and e.asExpr() = c.getQualifier()
+ or
+ argIdx >= 0 and e.asExpr() = c.getArgument(argIdx)
+ )
+ )
+ )
+ }
+}
+
+/**
+ * A negative characteristic that filters out non-public methods. Non-public methods are not interesting to include in
+ * the standard Java modeling, because they cannot be called from outside the package.
+ */
+private class NonPublicMethodCharacteristic extends CharacteristicsImpl::UninterestingToModelCharacteristic
+{
+ NonPublicMethodCharacteristic() { this = "non-public method" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not FrameworkCandidatesImpl::getCallable(e).isPublic()
+ }
+}
+
+/**
+ * Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
+ * characteristics. Lists the problematic characteristics and their implications for all such endpoints, together with
+ * an error message indicating why this combination is problematic.
+ *
+ * Copied from
+ * javascript/ql/experimental/adaptivethreatmodeling/test/endpoint_large_scale/ContradictoryEndpointCharacteristics.ql
+ */
+predicate erroneousEndpoints(
+ Endpoint endpoint, EndpointCharacteristic characteristic,
+ AutomodelEndpointTypes::EndpointType endpointType, float confidence, string errorMessage,
+ boolean ignoreKnownModelingErrors
+) {
+ // An endpoint's characteristics should not include positive indicators with medium/high confidence for more than one
+ // sink/source type (including the negative type).
+ exists(
+ EndpointCharacteristic characteristic2, AutomodelEndpointTypes::EndpointType endpointClass2,
+ float confidence2
+ |
+ endpointType != endpointClass2 and
+ (
+ endpointType instanceof AutomodelEndpointTypes::SinkType and
+ endpointClass2 instanceof AutomodelEndpointTypes::SinkType
+ or
+ endpointType instanceof AutomodelEndpointTypes::SourceType and
+ endpointClass2 instanceof AutomodelEndpointTypes::SourceType
+ ) and
+ characteristic.appliesToEndpoint(endpoint) and
+ characteristic2.appliesToEndpoint(endpoint) and
+ characteristic.hasImplications(endpointType, true, confidence) and
+ characteristic2.hasImplications(endpointClass2, true, confidence2) and
+ confidence > SharedCharacteristics::mediumConfidence() and
+ confidence2 > SharedCharacteristics::mediumConfidence() and
+ (
+ ignoreKnownModelingErrors = true and
+ not knownOverlappingCharacteristics(characteristic, characteristic2)
+ or
+ ignoreKnownModelingErrors = false
+ )
+ ) and
+ errorMessage = "Endpoint has high-confidence positive indicators for multiple classes"
+ or
+ // An endpoint's characteristics should not include positive indicators with medium/high confidence for some class and
+ // also include negative indicators with medium/high confidence for this same class.
+ exists(EndpointCharacteristic characteristic2, float confidence2 |
+ characteristic.appliesToEndpoint(endpoint) and
+ characteristic2.appliesToEndpoint(endpoint) and
+ characteristic.hasImplications(endpointType, true, confidence) and
+ characteristic2.hasImplications(endpointType, false, confidence2) and
+ confidence > SharedCharacteristics::mediumConfidence() and
+ confidence2 > SharedCharacteristics::mediumConfidence()
+ ) and
+ ignoreKnownModelingErrors = false and
+ errorMessage = "Endpoint has high-confidence positive and negative indicators for the same class"
+}
+
+/**
+ * Holds if `characteristic1` and `characteristic2` are among the pairs of currently known positive characteristics that
+ * have some overlap in their results. This indicates a problem with the underlying Java modeling. Specifically,
+ * `PathCreation` is prone to FPs.
+ */
+private predicate knownOverlappingCharacteristics(
+ EndpointCharacteristic characteristic1, EndpointCharacteristic characteristic2
+) {
+ characteristic1 != characteristic2 and
+ characteristic1 = ["mad taint step", "create path", "read file", "known non-sink"] and
+ characteristic2 = ["mad taint step", "create path", "read file", "known non-sink"]
+}
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
new file mode 100644
index 00000000000..14ba7b60799
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -0,0 +1,48 @@
+/**
+ * Surfaces the endpoints that are not already known to be sinks, and are therefore used as candidates for
+ * classification with an ML model.
+ *
+ * Note: This query does not actually classify the endpoints using the model.
+ *
+ * @name Automodel candidates
+ * @description A query to extract automodel candidates.
+ * @kind problem
+ * @severity info
+ * @id java/ml/extract-automodel-application-candidates
+ * @tags internal automodel extract candidates
+ */
+
+private import AutomodelApplicationModeCharacteristics
+private import AutomodelSharedUtil
+
+from
+ Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
+ boolean subtypes, string name, string signature, int input
+where
+ not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
+ u.appliesToEndpoint(endpoint)
+ ) and
+ // If a node is already a known sink for any of our existing ATM queries and is already modeled as a MaD sink, we
+ // don't include it as a candidate. Otherwise, we might include it as a candidate for query A, but the model will
+ // label it as a sink for one of the sink types of query B, for which it's already a known sink. This would result in
+ // overlap between our detected sinks and the pre-existing modeling. We assume that, if a sink has already been
+ // modeled in a MaD model, then it doesn't belong to any additional sink types, and we don't need to reexamine it.
+ not CharacteristicsImpl::isSink(endpoint, _) and
+ meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
+ // The message is the concatenation of all sink types for which this endpoint is known neither to be a sink nor to be
+ // a non-sink, and we surface only endpoints that have at least one such sink type.
+ message =
+ strictconcat(AutomodelEndpointTypes::SinkType sinkType |
+ not CharacteristicsImpl::isKnownSink(endpoint, sinkType) and
+ CharacteristicsImpl::isSinkCandidate(endpoint, sinkType)
+ |
+ sinkType, ", "
+ )
+select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
+ CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
+ package.(DollarAtString), "package", //
+ type.(DollarAtString), "type", //
+ subtypes.toString().(DollarAtString), "subtypes", //
+ name.(DollarAtString), "name", // method name
+ signature.(DollarAtString), "signature", //
+ input.toString().(DollarAtString), "input" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index a64327422a0..0fad9848efe 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -8,7 +8,7 @@
* @description A query to extract automodel candidates.
* @kind problem
* @severity info
- * @id java/ml/extract-automodel-candidates
+ * @id java/ml/extract-automodel-framework-candidates
* @tags internal automodel extract candidates
*/
From 9b30f9a4769d062d4f96de6b4d1081235bf034f4 Mon Sep 17 00:00:00 2001
From: Taus
Date: Mon, 22 May 2023 13:55:39 +0000
Subject: [PATCH 095/813] Java: Add negative characteristic for static calls
---
.../AutomodelApplicationModeCharacteristics.qll | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index b0dca8018a9..33745806763 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -290,6 +290,21 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
}
}
+/**
+ * A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
+ *are unlikely to have any non-trivial flow going into them.
+ */
+private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ ClassQualifierCharacteristic() { this = "class qualifier" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ exists(Call c |
+ e.asExpr() = c.getQualifier() and
+ c.getCallee().isStatic()
+ )
+ }
+}
+
/**
* Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
* characteristics. Lists the problematic characteristics and their implications for all such endpoints, together with
From 185ad101b31bc1cdaf9106a366592ebb3906e429 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 23 May 2023 10:34:01 +0200
Subject: [PATCH 096/813] Java: add application-mode and framework-mode tags to
extraction queries
---
.../src/Telemetry/AutomodelApplicationModeExtractCandidates.ql | 2 +-
.../ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql | 2 +-
.../Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql | 2 +-
.../Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
index 14ba7b60799..32665e77419 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -9,7 +9,7 @@
* @kind problem
* @severity info
* @id java/ml/extract-automodel-application-candidates
- * @tags internal automodel extract candidates
+ * @tags internal automodel extract candidates application-mode
*/
private import AutomodelApplicationModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index 0fad9848efe..1e9da50b72b 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -9,7 +9,7 @@
* @kind problem
* @severity info
* @id java/ml/extract-automodel-framework-candidates
- * @tags internal automodel extract candidates
+ * @tags internal automodel extract candidates framework-mode
*/
private import AutomodelFrameworkModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
index f1ba8ee4119..28d673df9ed 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
@@ -5,7 +5,7 @@
* @kind problem
* @severity info
* @id java/ml/non-sink
- * @tags internal automodel extract examples negative
+ * @tags internal automodel extract examples negative framework-mode
*/
private import AutomodelFrameworkModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
index e216c292538..b9c87d1dae6 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
@@ -5,7 +5,7 @@
* @kind problem
* @severity info
* @id java/ml/known-sink
- * @tags internal automodel extract examples positive
+ * @tags internal automodel extract examples positive framework-mode
*/
private import AutomodelFrameworkModeCharacteristics
From 7c3bc26c41309577bf492d44a4e4c96dafd66dd2 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 23 May 2023 12:02:02 +0000
Subject: [PATCH 097/813] Java: make input an actual string, not an integer
---
.../AutomodelApplicationModeCharacteristics.qll | 10 +++++-----
.../AutomodelApplicationModeExtractCandidates.ql | 4 ++--
.../AutomodelFrameworkModeCharacteristics.qll | 9 +++++----
.../AutomodelFrameworkModeExtractCandidates.ql | 4 ++--
.../AutomodelFrameworkModeExtractNegativeExamples.ql | 4 ++--
.../AutomodelFrameworkModeExtractPositiveExamples.ql | 4 ++--
6 files changed, 18 insertions(+), 17 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 33745806763..7b0a28c7a26 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -27,7 +27,7 @@ abstract class MetadataExtractor extends string {
abstract predicate hasMetadata(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- int input
+ string input
);
}
@@ -182,14 +182,14 @@ class FrameworkModeMetadataExtractor extends MetadataExtractor {
override predicate hasMetadata(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- int input
+ string input
) {
- exists(Call call, Callable callable |
+ exists(Call call, Callable callable, int argIdx |
call.getCallee() = callable and
(
- e.asExpr() = call.getArgument(input)
+ e.asExpr() = call.getArgument(argIdx) and input = "Argument[" + argIdx + "]"
or
- e.asExpr() = call.getQualifier() and input = -1
+ e.asExpr() = call.getQualifier() and argIdx = -1 and input = "Argument[this]"
) and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
index 32665e77419..43b2c2d626c 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -17,7 +17,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, int input
+ boolean subtypes, string name, string signature, string input
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
@@ -45,4 +45,4 @@ select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@,
subtypes.toString().(DollarAtString), "subtypes", //
name.(DollarAtString), "name", // method name
signature.(DollarAtString), "signature", //
- input.toString().(DollarAtString), "input" //
+ input.(DollarAtString), "input" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
index 57bd397f7a8..b3c9ea37e10 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
@@ -27,7 +27,7 @@ abstract class MetadataExtractor extends string {
abstract predicate hasMetadata(
DataFlow::ParameterNode e, string package, string type, boolean subtypes, string name,
- string signature, int input, string parameterName
+ string signature, string input, string parameterName
);
}
@@ -167,10 +167,11 @@ class FrameworkModeMetadataExtractor extends MetadataExtractor {
override predicate hasMetadata(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- int input, string parameterName
+ string input, string parameterName
) {
- exists(Callable callable |
- e.asParameter() = callable.getParameter(input) and
+ exists(Callable callable, int paramIdx |
+ e.asParameter() = callable.getParameter(paramIdx) and
+ (if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]") and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
subtypes = this.considerSubtypes(callable) and
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index 1e9da50b72b..488ba532920 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -17,7 +17,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, int input, string parameterName
+ boolean subtypes, string name, string signature, string input, string parameterName
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
@@ -47,5 +47,5 @@ select endpoint,
subtypes.toString().(DollarAtString), "subtypes", //
name.(DollarAtString), "name", //
signature.(DollarAtString), "signature", //
- input.toString().(DollarAtString), "input", //
+ input.(DollarAtString), "input", //
parameterName.(DollarAtString), "parameterName" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
index 28d673df9ed..9c1076ae05d 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
@@ -15,7 +15,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
MetadataExtractor meta, string package, string type, boolean subtypes, string name,
- string signature, int input, string parameterName
+ string signature, string input, string parameterName
where
characteristic.appliesToEndpoint(endpoint) and
confidence >= SharedCharacteristics::highConfidence() and
@@ -44,5 +44,5 @@ select endpoint,
subtypes.toString().(DollarAtString), "subtypes", //
name.(DollarAtString), "name", //
signature.(DollarAtString), "signature", //
- input.toString().(DollarAtString), "input", //
+ input.(DollarAtString), "input", //
parameterName.(DollarAtString), "parameterName" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
index b9c87d1dae6..d6c4926bbac 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
@@ -14,7 +14,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, SinkType sinkType, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, int input, string parameterName
+ boolean subtypes, string name, string signature, string input, string parameterName
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
@@ -31,5 +31,5 @@ select endpoint,
subtypes.toString().(DollarAtString), "subtypes", //
name.(DollarAtString), "name", //
signature.(DollarAtString), "signature", //
- input.toString().(DollarAtString), "input", //
+ input.(DollarAtString), "input", //
parameterName.(DollarAtString), "parameterName" //
From 6e21f14c0971f4f6097a63cfcdfffd3f1e73f711 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 23 May 2023 12:17:17 +0000
Subject: [PATCH 098/813] Java: update extraction query metadata
---
...tomodelApplicationModeExtractCandidates.ql | 6 +--
...lApplicationModeExtractNegativeExamples.ql | 45 +++++++++++++++++++
...lApplicationModeExtractPositiveExamples.ql | 32 +++++++++++++
...AutomodelFrameworkModeExtractCandidates.ql | 6 +--
...delFrameworkModeExtractNegativeExamples.ql | 6 +--
...delFrameworkModeExtractPositiveExamples.ql | 6 +--
6 files changed, 89 insertions(+), 12 deletions(-)
create mode 100644 java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
create mode 100644 java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
index 43b2c2d626c..1f4d12abbbc 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -4,12 +4,12 @@
*
* Note: This query does not actually classify the endpoints using the model.
*
- * @name Automodel candidates
- * @description A query to extract automodel candidates.
+ * @name Automodel candidates (application mode)
+ * @description A query to extract automodel candidates in application mode.
* @kind problem
* @severity info
* @id java/ml/extract-automodel-application-candidates
- * @tags internal automodel extract candidates application-mode
+ * @tags internal extract automodel application-mode candidates
*/
private import AutomodelApplicationModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
new file mode 100644
index 00000000000..3b3f1cd9c6f
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
@@ -0,0 +1,45 @@
+/**
+ * Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
+ *
+ * @name Negative examples (application mode)
+ * @kind problem
+ * @severity info
+ * @id java/ml/extract-automodel-application-negative-examples
+ * @tags internal extract automodel application-mode negative examples
+ */
+
+private import AutomodelApplicationModeCharacteristics
+private import AutomodelEndpointTypes
+private import AutomodelSharedUtil
+
+from
+ Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
+ MetadataExtractor meta, string package, string type, boolean subtypes, string name,
+ string signature, string input
+where
+ characteristic.appliesToEndpoint(endpoint) and
+ confidence >= SharedCharacteristics::highConfidence() and
+ characteristic.hasImplications(any(NegativeSinkType negative), true, confidence) and
+ // Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
+ // certain about in the prompt.
+ not erroneousEndpoints(endpoint, _, _, _, _, false) and
+ meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
+ // It's valid for a node to satisfy the logic for both `isSink` and `isSanitizer`, but in that case it will be
+ // treated by the actual query as a sanitizer, since the final logic is something like
+ // `isSink(n) and not isSanitizer(n)`. We don't want to include such nodes as negative examples in the prompt, because
+ // they're ambiguous and might confuse the model, so we explicitly exclude all known sinks from the negative examples.
+ not exists(EndpointCharacteristic characteristic2, float confidence2, SinkType positiveType |
+ not positiveType instanceof NegativeSinkType and
+ characteristic2.appliesToEndpoint(endpoint) and
+ confidence2 >= SharedCharacteristics::maximalConfidence() and
+ characteristic2.hasImplications(positiveType, true, confidence2)
+ ) and
+ message = characteristic
+select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
+ CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
+ package.(DollarAtString), "package", //
+ type.(DollarAtString), "type", //
+ subtypes.toString().(DollarAtString), "subtypes", //
+ name.(DollarAtString), "name", //
+ signature.(DollarAtString), "signature", //
+ input.(DollarAtString), "input" //
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
new file mode 100644
index 00000000000..37f3bb5cd69
--- /dev/null
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
@@ -0,0 +1,32 @@
+/**
+ * Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
+ *
+ * @name Positive examples (application mode)
+ * @kind problem
+ * @severity info
+ * @id java/ml/extract-automodel-application-positive-examples
+ * @tags internal extract automodel application-mode positive examples
+ */
+
+private import AutomodelApplicationModeCharacteristics
+private import AutomodelEndpointTypes
+private import AutomodelSharedUtil
+
+from
+ Endpoint endpoint, SinkType sinkType, MetadataExtractor meta, string package, string type,
+ boolean subtypes, string name, string signature, string input
+where
+ // Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
+ // certain about in the prompt.
+ not erroneousEndpoints(endpoint, _, _, _, _, false) and
+ meta.hasMetadata(endpoint, package, type, subtypes, name, signature, input) and
+ // Extract positive examples of sinks belonging to the existing ATM query configurations.
+ CharacteristicsImpl::isKnownSink(endpoint, sinkType)
+select endpoint, sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
+ CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
+ package.(DollarAtString), "package", //
+ type.(DollarAtString), "type", //
+ subtypes.toString().(DollarAtString), "subtypes", //
+ name.(DollarAtString), "name", //
+ signature.(DollarAtString), "signature", //
+ input.(DollarAtString), "input" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index 488ba532920..0f53399d2e3 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -4,12 +4,12 @@
*
* Note: This query does not actually classify the endpoints using the model.
*
- * @name Automodel candidates
- * @description A query to extract automodel candidates.
+ * @name Automodel candidates (framework mode)
+ * @description A query to extract automodel candidates in framework mode.
* @kind problem
* @severity info
* @id java/ml/extract-automodel-framework-candidates
- * @tags internal automodel extract candidates framework-mode
+ * @tags internal extract automodel framework-mode candidates
*/
private import AutomodelFrameworkModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
index 9c1076ae05d..9ecc1636c60 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
@@ -1,11 +1,11 @@
/**
* Surfaces endpoints that are non-sinks with high confidence, for use as negative examples in the prompt.
*
- * @name Negative examples (experimental)
+ * @name Negative examples (framework mode)
* @kind problem
* @severity info
- * @id java/ml/non-sink
- * @tags internal automodel extract examples negative framework-mode
+ * @id java/ml/extract-automodel-framework-negative-examples
+ * @tags internal extract automodel framework-mode negative examples
*/
private import AutomodelFrameworkModeCharacteristics
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
index d6c4926bbac..f03bb995a19 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
@@ -1,11 +1,11 @@
/**
* Surfaces endpoints that are sinks with high confidence, for use as positive examples in the prompt.
*
- * @name Positive examples (experimental)
+ * @name Positive examples (framework mode)
* @kind problem
* @severity info
- * @id java/ml/known-sink
- * @tags internal automodel extract examples positive framework-mode
+ * @id java/ml/extract-automodel-framework-positive-examples
+ * @tags internal extract automodel framework-mode positive examples
*/
private import AutomodelFrameworkModeCharacteristics
From d93ad9b398d91dbf6c36c1bed287491d00ababa5 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 23 May 2023 12:40:00 +0000
Subject: [PATCH 099/813] Java: remove unneeded abstract metadata extractor
classes and fix some names
---
...utomodelApplicationModeCharacteristics.qll | 47 +++++++------------
...tomodelApplicationModeExtractCandidates.ql | 4 +-
...lApplicationModeExtractNegativeExamples.ql | 2 +-
...lApplicationModeExtractPositiveExamples.ql | 4 +-
.../AutomodelFrameworkModeCharacteristics.qll | 18 +------
...AutomodelFrameworkModeExtractCandidates.ql | 4 +-
...delFrameworkModeExtractNegativeExamples.ql | 2 +-
...delFrameworkModeExtractPositiveExamples.ql | 4 +-
8 files changed, 29 insertions(+), 56 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 7b0a28c7a26..d8789dd1a17 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -17,20 +17,6 @@ private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclus
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
-/**
- * A meta data extractor. Any Java extraction mode needs to implement exactly
- * one instance of this class.
- */
-abstract class MetadataExtractor extends string {
- bindingset[this]
- MetadataExtractor() { any() }
-
- abstract predicate hasMetadata(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
- string input
- );
-}
-
newtype JavaRelatedLocationType = CallContext()
/**
@@ -41,14 +27,14 @@ private class ArgumentNode extends DataFlow::Node {
}
/**
- * A candidates implementation for framework mode.
+ * A candidates implementation.
*
* Some important notes:
* - This mode is using parameters as endpoints.
* - Sink- and neutral-information is being used from MaD models.
* - When available, we use method- and class-java-docs as related locations.
*/
-module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
+module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
class Endpoint = ArgumentNode;
@@ -108,7 +94,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
additional predicate sinkSpec(
Endpoint e, string package, string type, string name, string signature, string ext, string input
) {
- FrameworkCandidatesImpl::getCallable(e).hasQualifiedName(package, type, name) and
+ ApplicationCandidatesImpl::getCallable(e).hasQualifiedName(package, type, name) and
signature = ExternalFlow::paramsString(getCallable(e)) and
ext = "" and
(
@@ -147,21 +133,22 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
}
}
-module CharacteristicsImpl = SharedCharacteristics::SharedCharacteristics;
+module CharacteristicsImpl =
+ SharedCharacteristics::SharedCharacteristics;
class EndpointCharacteristic = CharacteristicsImpl::EndpointCharacteristic;
-class Endpoint = FrameworkCandidatesImpl::Endpoint;
+class Endpoint = ApplicationCandidatesImpl::Endpoint;
/*
* Predicates that are used to surface prompt examples and candidates for classification with an ML model.
*/
/**
- * A MetadataExtractor that extracts metadata for framework mode.
+ * A MetadataExtractor that extracts metadata for application mode.
*/
-class FrameworkModeMetadataExtractor extends MetadataExtractor {
- FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
+class ApplicationModeMetadataExtractor extends string {
+ ApplicationModeMetadataExtractor() { this = "ApplicationModeMetadataExtractor" }
/**
* By convention, the subtypes property of the MaD declaration should only be
@@ -180,7 +167,7 @@ class FrameworkModeMetadataExtractor extends MetadataExtractor {
else result = true
}
- override predicate hasMetadata(
+ predicate hasMetadata(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string input
) {
@@ -217,9 +204,9 @@ private class UnexploitableIsCharacteristic extends CharacteristicsImpl::NotASin
UnexploitableIsCharacteristic() { this = "unexploitable (is-style boolean method)" }
override predicate appliesToEndpoint(Endpoint e) {
- not FrameworkCandidatesImpl::isSink(e, _) and
- FrameworkCandidatesImpl::getCallable(e).getName().matches("is%") and
- FrameworkCandidatesImpl::getCallable(e).getReturnType() instanceof BooleanType
+ not ApplicationCandidatesImpl::isSink(e, _) and
+ ApplicationCandidatesImpl::getCallable(e).getName().matches("is%") and
+ ApplicationCandidatesImpl::getCallable(e).getReturnType() instanceof BooleanType
}
}
@@ -235,9 +222,9 @@ private class UnexploitableExistsCharacteristic extends CharacteristicsImpl::Not
UnexploitableExistsCharacteristic() { this = "unexploitable (existence-checking boolean method)" }
override predicate appliesToEndpoint(Endpoint e) {
- not FrameworkCandidatesImpl::isSink(e, _) and
+ not ApplicationCandidatesImpl::isSink(e, _) and
exists(Callable callable |
- callable = FrameworkCandidatesImpl::getCallable(e) and
+ callable = ApplicationCandidatesImpl::getCallable(e) and
callable.getName().toLowerCase() = ["exists", "notexists"] and
callable.getReturnType() instanceof BooleanType
)
@@ -251,7 +238,7 @@ private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkChara
ExceptionCharacteristic() { this = "exception" }
override predicate appliesToEndpoint(Endpoint e) {
- FrameworkCandidatesImpl::getCallable(e).getDeclaringType().getASupertype*() instanceof
+ ApplicationCandidatesImpl::getCallable(e).getDeclaringType().getASupertype*() instanceof
TypeThrowable
}
}
@@ -286,7 +273,7 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
NonPublicMethodCharacteristic() { this = "non-public method" }
override predicate appliesToEndpoint(Endpoint e) {
- not FrameworkCandidatesImpl::getCallable(e).isPublic()
+ not ApplicationCandidatesImpl::getCallable(e).isPublic()
}
}
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
index 1f4d12abbbc..40e0e307300 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -16,8 +16,8 @@ private import AutomodelApplicationModeCharacteristics
private import AutomodelSharedUtil
from
- Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, string input
+ Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, string package,
+ string type, boolean subtypes, string name, string signature, string input
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
index 3b3f1cd9c6f..5e12786e106 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
@@ -14,7 +14,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
- MetadataExtractor meta, string package, string type, boolean subtypes, string name,
+ ApplicationModeMetadataExtractor meta, string package, string type, boolean subtypes, string name,
string signature, string input
where
characteristic.appliesToEndpoint(endpoint) and
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
index 37f3bb5cd69..0a70b376c82 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
@@ -13,8 +13,8 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
from
- Endpoint endpoint, SinkType sinkType, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, string input
+ Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta, string package,
+ string type, boolean subtypes, string name, string signature, string input
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
index b3c9ea37e10..84a725931a5 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
@@ -17,20 +17,6 @@ private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclus
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
-/**
- * A meta data extractor. Any Java extraction mode needs to implement exactly
- * one instance of this class.
- */
-abstract class MetadataExtractor extends string {
- bindingset[this]
- MetadataExtractor() { any() }
-
- abstract predicate hasMetadata(
- DataFlow::ParameterNode e, string package, string type, boolean subtypes, string name,
- string signature, string input, string parameterName
- );
-}
-
newtype JavaRelatedLocationType =
MethodDoc() or
ClassDoc()
@@ -145,7 +131,7 @@ class Endpoint = FrameworkCandidatesImpl::Endpoint;
/**
* A MetadataExtractor that extracts metadata for framework mode.
*/
-class FrameworkModeMetadataExtractor extends MetadataExtractor {
+class FrameworkModeMetadataExtractor extends string {
FrameworkModeMetadataExtractor() { this = "FrameworkModeMetadataExtractor" }
/**
@@ -165,7 +151,7 @@ class FrameworkModeMetadataExtractor extends MetadataExtractor {
else result = true
}
- override predicate hasMetadata(
+ predicate hasMetadata(
Endpoint e, string package, string type, boolean subtypes, string name, string signature,
string input, string parameterName
) {
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index 0f53399d2e3..7f7f050fac4 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -16,8 +16,8 @@ private import AutomodelFrameworkModeCharacteristics
private import AutomodelSharedUtil
from
- Endpoint endpoint, string message, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, string input, string parameterName
+ Endpoint endpoint, string message, FrameworkModeMetadataExtractor meta, string package,
+ string type, boolean subtypes, string name, string signature, string input, string parameterName
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
index 9ecc1636c60..9036d638c2b 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
@@ -14,7 +14,7 @@ private import AutomodelSharedUtil
from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
- MetadataExtractor meta, string package, string type, boolean subtypes, string name,
+ FrameworkModeMetadataExtractor meta, string package, string type, boolean subtypes, string name,
string signature, string input, string parameterName
where
characteristic.appliesToEndpoint(endpoint) and
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
index f03bb995a19..eeaf7037a15 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
@@ -13,8 +13,8 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
from
- Endpoint endpoint, SinkType sinkType, MetadataExtractor meta, string package, string type,
- boolean subtypes, string name, string signature, string input, string parameterName
+ Endpoint endpoint, SinkType sinkType, FrameworkModeMetadataExtractor meta, string package,
+ string type, boolean subtypes, string name, string signature, string input, string parameterName
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
From db61a2d09920f038f95e1adecdeee9ba2b027a91 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Tue, 23 May 2023 12:53:27 +0000
Subject: [PATCH 100/813] Java: share isKnownKind between modes
---
...utomodelApplicationModeCharacteristics.qll | 30 ++-----------------
.../AutomodelFrameworkModeCharacteristics.qll | 27 ++---------------
java/ql/src/Telemetry/AutomodelSharedUtil.qll | 30 +++++++++++++++++++
3 files changed, 34 insertions(+), 53 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index d8789dd1a17..8c13cecb774 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -14,6 +14,7 @@ private import semmle.code.java.Expr as Expr
private import semmle.code.java.security.QueryInjection
private import semmle.code.java.security.RequestForgery
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
+private import AutomodelSharedUtil as AutomodelSharedUtil
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
@@ -51,31 +52,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
- predicate isKnownKind(string kind, string humanReadableKind, EndpointType type) {
- kind = "read-file" and
- humanReadableKind = "read file" and
- type instanceof AutomodelEndpointTypes::TaintedPathSinkType
- or
- kind = "create-file" and
- humanReadableKind = "create file" and
- type instanceof AutomodelEndpointTypes::TaintedPathSinkType
- or
- kind = "sql" and
- humanReadableKind = "mad modeled sql" and
- type instanceof AutomodelEndpointTypes::SqlSinkType
- or
- kind = "open-url" and
- humanReadableKind = "open url" and
- type instanceof AutomodelEndpointTypes::RequestForgerySinkType
- or
- kind = "jdbc-url" and
- humanReadableKind = "jdbc url" and
- type instanceof AutomodelEndpointTypes::RequestForgerySinkType
- or
- kind = "command-injection" and
- humanReadableKind = "command injection" and
- type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
- }
+ predicate isKnownKind = AutomodelSharedUtil::isKnownKind/3;
predicate isSink(Endpoint e, string kind) {
exists(string package, string type, string name, string signature, string ext, string input |
@@ -105,9 +82,6 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
or
exists(Call c | e.asExpr() = c.getQualifier() and input = "Argument[this]")
)
- // exists(int paramIdx | e.isParameterOf(_, paramIdx) |
- // if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]"
- // )
}
/**
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
index 84a725931a5..19293e26e79 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
@@ -14,6 +14,7 @@ private import semmle.code.java.Expr as Expr
private import semmle.code.java.security.QueryInjection
private import semmle.code.java.security.RequestForgery
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
+private import AutomodelSharedUtil as AutomodelSharedUtil
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
@@ -46,31 +47,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
RelatedLocation asLocation(Endpoint e) { result = e.asParameter() }
- predicate isKnownKind(string kind, string humanReadableKind, EndpointType type) {
- kind = "read-file" and
- humanReadableKind = "read file" and
- type instanceof AutomodelEndpointTypes::TaintedPathSinkType
- or
- kind = "create-file" and
- humanReadableKind = "create file" and
- type instanceof AutomodelEndpointTypes::TaintedPathSinkType
- or
- kind = "sql" and
- humanReadableKind = "mad modeled sql" and
- type instanceof AutomodelEndpointTypes::SqlSinkType
- or
- kind = "open-url" and
- humanReadableKind = "open url" and
- type instanceof AutomodelEndpointTypes::RequestForgerySinkType
- or
- kind = "jdbc-url" and
- humanReadableKind = "jdbc url" and
- type instanceof AutomodelEndpointTypes::RequestForgerySinkType
- or
- kind = "command-injection" and
- humanReadableKind = "command injection" and
- type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
- }
+ predicate isKnownKind = AutomodelSharedUtil::isKnownKind/3;
predicate isSink(Endpoint e, string kind) {
exists(string package, string type, string name, string signature, string ext, string input |
diff --git a/java/ql/src/Telemetry/AutomodelSharedUtil.qll b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
index e03e46abd1d..e3a2799be51 100644
--- a/java/ql/src/Telemetry/AutomodelSharedUtil.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
@@ -1,3 +1,5 @@
+import AutomodelEndpointTypes as AutomodelEndpointTypes
+
/**
* A helper class to represent a string value that can be returned by a query using $@ notation.
*
@@ -19,3 +21,31 @@ class DollarAtString extends string {
path = this and sl = 1 and sc = 1 and el = 1 and ec = 1
}
}
+
+predicate isKnownKind(
+ string kind, string humanReadableKind, AutomodelEndpointTypes::EndpointType type
+) {
+ kind = "read-file" and
+ humanReadableKind = "read file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ kind = "create-file" and
+ humanReadableKind = "create file" and
+ type instanceof AutomodelEndpointTypes::TaintedPathSinkType
+ or
+ kind = "sql" and
+ humanReadableKind = "mad modeled sql" and
+ type instanceof AutomodelEndpointTypes::SqlSinkType
+ or
+ kind = "open-url" and
+ humanReadableKind = "open url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ kind = "jdbc-url" and
+ humanReadableKind = "jdbc url" and
+ type instanceof AutomodelEndpointTypes::RequestForgerySinkType
+ or
+ kind = "command-injection" and
+ humanReadableKind = "command injection" and
+ type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
+}
From 04b8bf35d4055595dd580c695b3fa4e1be199012 Mon Sep 17 00:00:00 2001
From: Taus
Date: Tue, 23 May 2023 13:16:01 +0000
Subject: [PATCH 101/813] Java: Avoid overlapping import
Importing `AutomodelEndpointTypes` inside `AutomodelSharedUtil` non-privately made it overlap with the imports in the candidate extraction queries.
---
java/ql/src/Telemetry/AutomodelSharedUtil.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelSharedUtil.qll b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
index e3a2799be51..9500b4ac635 100644
--- a/java/ql/src/Telemetry/AutomodelSharedUtil.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
@@ -1,4 +1,4 @@
-import AutomodelEndpointTypes as AutomodelEndpointTypes
+private import AutomodelEndpointTypes as AutomodelEndpointTypes
/**
* A helper class to represent a string value that can be returned by a query using $@ notation.
From 11ab7e2e7108603b83440ffe0aa53c35458e8dd2 Mon Sep 17 00:00:00 2001
From: Taus
Date: Tue, 23 May 2023 14:04:09 +0000
Subject: [PATCH 102/813] Java: Share argument indexing logic
Adds a utility predicate for turning integer indices into the desired string representation.
---
.../AutomodelApplicationModeCharacteristics.qll | 13 ++++++++-----
.../AutomodelFrameworkModeCharacteristics.qll | 4 ++--
java/ql/src/Telemetry/AutomodelSharedUtil.qll | 8 ++++++++
3 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 8c13cecb774..223a13127d2 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -77,10 +77,12 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
(
exists(Call c, int argIdx |
e.asExpr() = c.getArgument(argIdx) and
- input = "Argument[" + argIdx + "]"
+ input = AutomodelSharedUtil::getArgumentForIndex(argIdx)
)
or
- exists(Call c | e.asExpr() = c.getQualifier() and input = "Argument[this]")
+ exists(Call c |
+ e.asExpr() = c.getQualifier() and input = AutomodelSharedUtil::getArgumentForIndex(-1)
+ )
)
}
@@ -148,10 +150,11 @@ class ApplicationModeMetadataExtractor extends string {
exists(Call call, Callable callable, int argIdx |
call.getCallee() = callable and
(
- e.asExpr() = call.getArgument(argIdx) and input = "Argument[" + argIdx + "]"
+ e.asExpr() = call.getArgument(argIdx)
or
- e.asExpr() = call.getQualifier() and argIdx = -1 and input = "Argument[this]"
+ e.asExpr() = call.getQualifier() and argIdx = -1
) and
+ input = AutomodelSharedUtil::getArgumentForIndex(argIdx) and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
subtypes = this.considerSubtypes(callable) and
@@ -231,7 +234,7 @@ private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingTo
exists(int argIdx | exists(api.getParameter(argIdx)) |
argIdx = -1 and e.asExpr() = c.getQualifier()
or
- argIdx >= 0 and e.asExpr() = c.getArgument(argIdx)
+ e.asExpr() = c.getArgument(argIdx)
)
)
)
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
index 19293e26e79..85e28436df3 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
@@ -70,7 +70,7 @@ module FrameworkCandidatesImpl implements SharedCharacteristics::CandidateSig {
signature = ExternalFlow::paramsString(getCallable(e)) and
ext = "" and
exists(int paramIdx | e.isParameterOf(_, paramIdx) |
- if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]"
+ input = AutomodelSharedUtil::getArgumentForIndex(paramIdx)
)
}
@@ -134,7 +134,7 @@ class FrameworkModeMetadataExtractor extends string {
) {
exists(Callable callable, int paramIdx |
e.asParameter() = callable.getParameter(paramIdx) and
- (if paramIdx = -1 then input = "Argument[this]" else input = "Argument[" + paramIdx + "]") and
+ input = AutomodelSharedUtil::getArgumentForIndex(paramIdx) and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
subtypes = this.considerSubtypes(callable) and
diff --git a/java/ql/src/Telemetry/AutomodelSharedUtil.qll b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
index 9500b4ac635..2ca38506160 100644
--- a/java/ql/src/Telemetry/AutomodelSharedUtil.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
@@ -49,3 +49,11 @@ predicate isKnownKind(
humanReadableKind = "command injection" and
type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
}
+
+/** Gets the argument name for the argument with the index `index`. */
+bindingset[index]
+string getArgumentForIndex(int index) {
+ index = -1 and result = "Argument[this]"
+ or
+ index >= 0 and result = "Argument[" + index + "]"
+}
From 2000f2253320429c8bc6752cff91d43a77bb04a9 Mon Sep 17 00:00:00 2001
From: Taus
Date: Wed, 24 May 2023 13:57:02 +0000
Subject: [PATCH 103/813] Java: Port over characteristics from codex branch
---
...utomodelApplicationModeCharacteristics.qll | 112 ++++++++++++++++--
1 file changed, 105 insertions(+), 7 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 223a13127d2..13b494c377e 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -59,6 +59,8 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
sinkSpec(e, package, type, name, signature, ext, input) and
ExternalFlow::sinkModel(package, type, _, name, [signature, ""], ext, input, kind, _)
)
+ or
+ isCustomSink(e, kind)
}
predicate isNeutral(Endpoint e) {
@@ -109,6 +111,18 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
}
}
+/**
+ * Contains endpoints that are defined in QL code rather than as a MaD model. Ideally this predicate
+ * should be empty.
+ */
+private predicate isCustomSink(Endpoint e, string kind) {
+ e.asExpr() instanceof ArgumentToExec and kind = "command injection"
+ or
+ e instanceof RequestForgerySink and kind = "request forgery"
+ or
+ e instanceof QueryInjectionSink and kind = "sql"
+}
+
module CharacteristicsImpl =
SharedCharacteristics::SharedCharacteristics;
@@ -220,6 +234,37 @@ private class ExceptionCharacteristic extends CharacteristicsImpl::NotASinkChara
}
}
+/**
+ * A negative characteristic that indicates that an endpoint is a MaD taint step. MaD modeled taint steps are global,
+ * so they are not sinks for any query. Non-MaD taint steps might be specific to a particular query, so we don't
+ * filter those out.
+ */
+private class IsMaDTaintStepCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ IsMaDTaintStepCharacteristic() { this = "taint step" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ FlowSummaryImpl::Private::Steps::summaryThroughStepValue(e, _, _) or
+ FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(e, _, _) or
+ FlowSummaryImpl::Private::Steps::summaryGetterStep(e, _, _, _) or
+ FlowSummaryImpl::Private::Steps::summarySetterStep(e, _, _, _)
+ }
+}
+
+/**
+ * A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
+ *are unlikely to have any non-trivial flow going into them.
+ */
+private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
+ ClassQualifierCharacteristic() { this = "class qualifier" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ exists(Call c |
+ e.asExpr() = c.getQualifier() and
+ c.getQualifier() instanceof TypeAccess
+ )
+ }
+}
+
/**
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
* are considered worth modeling.
@@ -255,20 +300,73 @@ private class NonPublicMethodCharacteristic extends CharacteristicsImpl::Uninter
}
/**
- * A negative characteristic that filters out qualifiers that are classes (i.e. static calls). These
- *are unlikely to have any non-trivial flow going into them.
+ * A negative characteristic that indicates that an endpoint is a non-sink argument to a method whose sinks have already
+ * been modeled.
+ *
+ * WARNING: These endpoints should not be used as negative samples for training, because some sinks may have been missed
+ * when the method was modeled. Specifically, as we start using ATM to merge in new declarations, we can be less sure
+ * that a method with one argument modeled as a MaD sink has also had its remaining arguments manually reviewed. The
+ * ML model might have predicted argument 0 of some method to be a sink but not argument 1, when in fact argument 1 is
+ * also a sink.
*/
-private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASinkCharacteristic {
- ClassQualifierCharacteristic() { this = "class qualifier" }
+private class OtherArgumentToModeledMethodCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
+{
+ OtherArgumentToModeledMethodCharacteristic() {
+ this = "other argument to a method that has already been modeled"
+ }
override predicate appliesToEndpoint(Endpoint e) {
- exists(Call c |
- e.asExpr() = c.getQualifier() and
- c.getCallee().isStatic()
+ not ApplicationCandidatesImpl::isSink(e, _) and
+ exists(DataFlow::Node otherSink |
+ ApplicationCandidatesImpl::isSink(otherSink, _) and
+ e.asExpr() = otherSink.asExpr().(Argument).getCall().getAnArgument() and
+ e != otherSink
)
}
}
+/**
+ * A negative characteristic that indicates that an endpoint is not part of the source code for the project being
+ * analyzed.
+ *
+ * WARNING: These endpoints should not be used as negative samples for training, because they are not necessarily
+ * non-sinks. They are merely not interesting sinks to run through the ML model.
+ *
+ * TODO: Check that this actually does anything.
+ */
+private class IsExternalCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
+ IsExternalCharacteristic() { this = "external" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ not exists(e.getLocation().getFile().getRelativePath())
+ }
+}
+
+/**
+ * A negative characteristic that indicates that an endpoint is not a `to` node for any known taint step. Such a node
+ * cannot be tainted, because taint can't flow into it.
+ *
+ * WARNING: These endpoints should not be used as negative samples for training, because they may include sinks for
+ * which our taint tracking modeling is incomplete.
+ */
+private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic
+{
+ CannotBeTaintedCharacteristic() { this = "cannot be tainted" }
+
+ override predicate appliesToEndpoint(Endpoint e) { not this.isKnownOutNodeForStep(e) }
+
+ /**
+ * Holds if the node `n` is known as the predecessor in a modeled flow step.
+ */
+ private predicate isKnownOutNodeForStep(Endpoint e) {
+ TaintTracking::localTaintStep(_, e) or
+ FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e, _) or
+ FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e, _) or
+ FlowSummaryImpl::Private::Steps::summaryGetterStep(_, _, e, _) or
+ FlowSummaryImpl::Private::Steps::summarySetterStep(_, _, e, _)
+ }
+}
+
/**
* Holds if the given endpoint has a self-contradictory combination of characteristics. Detects errors in our endpoint
* characteristics. Lists the problematic characteristics and their implications for all such endpoints, together with
From 33fdb0fc522a9e43aff2fda9203e8ccb417d165b Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 24 May 2023 16:09:25 +0200
Subject: [PATCH 104/813] Java: remove superfluous characteristic
---
.../AutomodelApplicationModeCharacteristics.qll | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 13b494c377e..e8bd53a8aa3 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -325,23 +325,6 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
}
}
-/**
- * A negative characteristic that indicates that an endpoint is not part of the source code for the project being
- * analyzed.
- *
- * WARNING: These endpoints should not be used as negative samples for training, because they are not necessarily
- * non-sinks. They are merely not interesting sinks to run through the ML model.
- *
- * TODO: Check that this actually does anything.
- */
-private class IsExternalCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
- IsExternalCharacteristic() { this = "external" }
-
- override predicate appliesToEndpoint(Endpoint e) {
- not exists(e.getLocation().getFile().getRelativePath())
- }
-}
-
/**
* A negative characteristic that indicates that an endpoint is not a `to` node for any known taint step. Such a node
* cannot be tainted, because taint can't flow into it.
From f224a40dec00cb17c6a4fbc2e480a20b6334bb5b Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 25 May 2023 09:53:19 +0200
Subject: [PATCH 105/813] Java: use containing call as call context, not
argument
---
.../src/Telemetry/AutomodelApplicationModeCharacteristics.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index e8bd53a8aa3..6e173e8232b 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -95,7 +95,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
*/
RelatedLocation getRelatedLocation(Endpoint e, RelatedLocationType type) {
type = CallContext() and
- result = asLocation(e)
+ result = any(Call c | e.asExpr() = [c.getAnArgument(), c.getQualifier()])
}
/**
From 9a041243ffc4ca2b2005971da52c053f49802a63 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 25 May 2023 13:53:52 +0200
Subject: [PATCH 106/813] Java: fine-tune characteristics
---
...utomodelApplicationModeCharacteristics.qll | 27 ++++++++++---------
1 file changed, 15 insertions(+), 12 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 6e173e8232b..736e8b947bf 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -269,20 +269,23 @@ private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASink
* A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
* are considered worth modeling.
*/
-private class NotAModelApiParameter extends CharacteristicsImpl::UninterestingToModelCharacteristic {
- NotAModelApiParameter() { this = "not a model API parameter" }
+private class ArgumentToLocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
+ ArgumentToLocalCall() { this = "argument to local call" }
override predicate appliesToEndpoint(Endpoint e) {
- not exists(ModelExclusions::ModelApi api |
- exists(Call c |
- c.getCallee() = api and
- exists(int argIdx | exists(api.getParameter(argIdx)) |
- argIdx = -1 and e.asExpr() = c.getQualifier()
- or
- e.asExpr() = c.getArgument(argIdx)
- )
- )
- )
+ ApplicationCandidatesImpl::getCallable(e).fromSource()
+ }
+}
+
+/**
+ * A Characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module.
+ */
+private class ExcludedFromModeling extends CharacteristicsImpl::UninterestingToModelCharacteristic {
+ ExcludedFromModeling() { this = "excluded from modeling" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ ModelExclusions::isUninterestingForModels(ApplicationCandidatesImpl::getCallable(e)) or
+ ModelExclusions::isUninterestingForModels(e.getEnclosingCallable())
}
}
From 791ba81403721610f45f8d601413f78d2245d5a5 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 25 May 2023 13:23:52 +0100
Subject: [PATCH 107/813] Swift: Add change note.
---
.../change-notes/2023-05-25-string-length-conflation-fp.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
diff --git a/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md b/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
new file mode 100644
index 00000000000..937ebd4b41a
--- /dev/null
+++ b/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
@@ -0,0 +1,4 @@
+—
+category: minorAnalysis
+—
+* Fixed some false positive results from the `swift/string-length-conflation` query, caused by imprecise sinks.
From 242d263e8a11617492d7375193f7841521306431 Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 15:18:15 +0200
Subject: [PATCH 108/813] Codegen: move ipa info from `ql.Class` to
`ql.Property`
---
misc/codegen/generators/qlgen.py | 6 +++---
misc/codegen/lib/ql.py | 2 +-
misc/codegen/templates/ql_class.mustache | 16 ++++++++--------
misc/codegen/test/test_qlgen.py | 16 ++++++++++++----
4 files changed, 24 insertions(+), 16 deletions(-)
diff --git a/misc/codegen/generators/qlgen.py b/misc/codegen/generators/qlgen.py
index 7affea51828..49b1b537bc2 100755
--- a/misc/codegen/generators/qlgen.py
+++ b/misc/codegen/generators/qlgen.py
@@ -110,7 +110,8 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, prev_child: str =
is_optional=prop.is_optional,
is_predicate=prop.is_predicate,
is_unordered=prop.is_unordered,
- description=prop.description
+ description=prop.description,
+ synth=bool(cls.ipa),
)
if prop.is_single:
args.update(
@@ -162,7 +163,6 @@ def get_ql_class(cls: schema.Class) -> ql.Class:
final=not cls.derived,
properties=properties,
dir=pathlib.Path(cls.group or ""),
- ipa=bool(cls.ipa),
doc=cls.doc,
**pragmas,
)
@@ -342,7 +342,7 @@ def generate(opts, renderer):
with renderer.manage(generated=generated, stubs=stubs, registry=opts.generated_registry,
force=opts.force) as renderer:
- db_classes = [cls for cls in classes.values() if not cls.ipa]
+ db_classes = [cls for name, cls in classes.items() if not data.classes[name].ipa]
renderer.render(ql.DbClasses(db_classes), out / "Raw.qll")
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
diff --git a/misc/codegen/lib/ql.py b/misc/codegen/lib/ql.py
index 97165053fd0..e4bac3197cb 100644
--- a/misc/codegen/lib/ql.py
+++ b/misc/codegen/lib/ql.py
@@ -42,6 +42,7 @@ class Property:
description: List[str] = field(default_factory=list)
doc: Optional[str] = None
doc_plural: Optional[str] = None
+ synth: bool = False
def __post_init__(self):
if self.tableparams:
@@ -111,7 +112,6 @@ class Class:
qltest_collapse_hierarchy: bool = False
qltest_uncollapse_hierarchy: bool = False
ql_internal: bool = False
- ipa: bool = False
doc: List[str] = field(default_factory=list)
def __post_init__(self):
diff --git a/misc/codegen/templates/ql_class.mustache b/misc/codegen/templates/ql_class.mustache
index 9f72caef392..8ee465bf993 100644
--- a/misc/codegen/templates/ql_class.mustache
+++ b/misc/codegen/templates/ql_class.mustache
@@ -67,12 +67,12 @@ module Generated {
* behavior of both the `Immediate` and non-`Immediate` versions.
*/
{{type}} get{{#is_unordered}}An{{/is_unordered}}Immediate{{singular}}({{#is_indexed}}int index{{/is_indexed}}) {
- {{^ipa}}
+ {{^synth}}
result = Synth::convert{{type}}FromRaw(Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}}))
- {{/ipa}}
- {{#ipa}}
+ {{/synth}}
+ {{#synth}}
none()
- {{/ipa}}
+ {{/synth}}
}
/**
@@ -99,12 +99,12 @@ module Generated {
{{/has_description}}
*/
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
- {{^ipa}}
+ {{^synth}}
{{^is_predicate}}result = {{/is_predicate}}Synth::convert{{name}}ToRaw(this){{^root}}.(Raw::{{name}}){{/root}}.{{getter}}({{#is_indexed}}index{{/is_indexed}})
- {{/ipa}}
- {{#ipa}}
+ {{/synth}}
+ {{#synth}}
none()
- {{/ipa}}
+ {{/synth}}
}
{{/type_is_class}}
diff --git a/misc/codegen/test/test_qlgen.py b/misc/codegen/test/test_qlgen.py
index 1cd85762315..6ed83edf4e0 100644
--- a/misc/codegen/test/test_qlgen.py
+++ b/misc/codegen/test/test_qlgen.py
@@ -861,25 +861,33 @@ def test_property_on_class_with_default_doc_name(generate_classes):
def test_stub_on_class_with_ipa_from_class(generate_classes):
assert generate_classes([
- schema.Class("MyObject", ipa=schema.IpaInfo(from_class="A")),
+ schema.Class("MyObject", ipa=schema.IpaInfo(from_class="A"),
+ properties=[schema.SingleProperty("foo", "bar")]),
]) == {
"MyObject.qll": (a_ql_stub(name="MyObject", base_import=gen_import_prefix + "MyObject", ipa_accessors=[
ql.IpaUnderlyingAccessor(argument="Entity", type="Raw::A", constructorparams=["result"]),
]),
- a_ql_class(name="MyObject", final=True, ipa=True)),
+ a_ql_class(name="MyObject", final=True, properties=[
+ ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
+ tableparams=["this", "result"], doc="foo of this my object"),
+ ])),
}
def test_stub_on_class_with_ipa_on_arguments(generate_classes):
assert generate_classes([
- schema.Class("MyObject", ipa=schema.IpaInfo(on_arguments={"base": "A", "index": "int", "label": "string"})),
+ schema.Class("MyObject", ipa=schema.IpaInfo(on_arguments={"base": "A", "index": "int", "label": "string"}),
+ properties=[schema.SingleProperty("foo", "bar")]),
]) == {
"MyObject.qll": (a_ql_stub(name="MyObject", base_import=gen_import_prefix + "MyObject", ipa_accessors=[
ql.IpaUnderlyingAccessor(argument="Base", type="Raw::A", constructorparams=["result", "_", "_"]),
ql.IpaUnderlyingAccessor(argument="Index", type="int", constructorparams=["_", "result", "_"]),
ql.IpaUnderlyingAccessor(argument="Label", type="string", constructorparams=["_", "_", "result"]),
]),
- a_ql_class(name="MyObject", final=True, ipa=True)),
+ a_ql_class(name="MyObject", final=True, properties=[
+ ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
+ tableparams=["this", "result"], doc="foo of this my object"),
+ ])),
}
From 165ac3eeaa2104b5947cc0869e9c9479abfa8e3c Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 15:28:53 +0200
Subject: [PATCH 109/813] Codegen: define and propagate `synth` property flag
---
misc/codegen/generators/qlgen.py | 2 +-
misc/codegen/lib/schema.py | 1 +
misc/codegen/test/test_qlgen.py | 14 ++++++++++++++
3 files changed, 16 insertions(+), 1 deletion(-)
diff --git a/misc/codegen/generators/qlgen.py b/misc/codegen/generators/qlgen.py
index 49b1b537bc2..a5b8379580f 100755
--- a/misc/codegen/generators/qlgen.py
+++ b/misc/codegen/generators/qlgen.py
@@ -111,7 +111,7 @@ def get_ql_property(cls: schema.Class, prop: schema.Property, prev_child: str =
is_predicate=prop.is_predicate,
is_unordered=prop.is_unordered,
description=prop.description,
- synth=bool(cls.ipa),
+ synth=bool(cls.ipa) or prop.synth,
)
if prop.is_single:
args.update(
diff --git a/misc/codegen/lib/schema.py b/misc/codegen/lib/schema.py
index 64a4720093b..2f3ce1cb9d3 100644
--- a/misc/codegen/lib/schema.py
+++ b/misc/codegen/lib/schema.py
@@ -34,6 +34,7 @@ class Property:
pragmas: List[str] = field(default_factory=list)
doc: Optional[str] = None
description: List[str] = field(default_factory=list)
+ synth: bool = False
@property
def is_single(self) -> bool:
diff --git a/misc/codegen/test/test_qlgen.py b/misc/codegen/test/test_qlgen.py
index 6ed83edf4e0..4650974510f 100644
--- a/misc/codegen/test/test_qlgen.py
+++ b/misc/codegen/test/test_qlgen.py
@@ -891,5 +891,19 @@ def test_stub_on_class_with_ipa_on_arguments(generate_classes):
}
+def test_synth_property(generate_classes):
+ assert generate_classes([
+ schema.Class("MyObject", properties=[
+ schema.SingleProperty("foo", "bar", synth=True)]),
+ ]) == {
+ "MyObject.qll": (a_ql_stub(name="MyObject", base_import=gen_import_prefix + "MyObject"),
+ a_ql_class(name="MyObject", final=True,
+ properties=[
+ ql.Property(singular="Foo", type="bar", tablename="my_objects", synth=True,
+ tableparams=["this", "result"], doc="foo of this my object"),
+ ])),
+ }
+
+
if __name__ == '__main__':
sys.exit(pytest.main([__file__] + sys.argv[1:]))
From d2c9847a790197ce1fe36b957f7ac511f3d630fa Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 15:36:32 +0200
Subject: [PATCH 110/813] Codegen: parse `synth` property modifier
---
misc/codegen/lib/schemadefs.py | 7 ++++++-
misc/codegen/test/test_schemaloader.py | 11 +++++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/misc/codegen/lib/schemadefs.py b/misc/codegen/lib/schemadefs.py
index f3bfd9840dc..3db8fe781ce 100644
--- a/misc/codegen/lib/schemadefs.py
+++ b/misc/codegen/lib/schemadefs.py
@@ -44,10 +44,15 @@ class _Namespace:
self.__dict__.update(kwargs)
+class _SynthModifier(_schema.PropertyModifier, _Namespace):
+ def modify(self, prop: _schema.Property):
+ prop.synth = True
+
+
qltest = _Namespace()
ql = _Namespace()
cpp = _Namespace()
-synth = _Namespace()
+synth = _SynthModifier()
@_dataclass
diff --git a/misc/codegen/test/test_schemaloader.py b/misc/codegen/test/test_schemaloader.py
index 9c9750818ea..949d2f385be 100644
--- a/misc/codegen/test/test_schemaloader.py
+++ b/misc/codegen/test/test_schemaloader.py
@@ -455,6 +455,17 @@ def test_ipa_class_hierarchy():
}
+def test_synthesized_property():
+ @load
+ class data:
+ class A:
+ x: defs.int | defs.synth
+
+ assert data.classes["A"].properties == [
+ schema.SingleProperty("x", "int", synth=True)
+ ]
+
+
def test_class_docstring():
@load
class data:
From 00fb796f3bd79243830bbb11b97e675c70adac43 Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 15:43:43 +0200
Subject: [PATCH 111/813] Codegen: ignore `synth` properties in `dbschemegen`
---
misc/codegen/generators/dbschemegen.py | 7 +++++--
misc/codegen/test/test_dbschemegen.py | 27 ++++++++++++++++++++++++++
2 files changed, 32 insertions(+), 2 deletions(-)
diff --git a/misc/codegen/generators/dbschemegen.py b/misc/codegen/generators/dbschemegen.py
index 8441b6fd61d..bc45ae3022f 100755
--- a/misc/codegen/generators/dbschemegen.py
+++ b/misc/codegen/generators/dbschemegen.py
@@ -49,7 +49,7 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
# Leaf classes need a table to bind the `@` ids
# 1-to-1 properties are added to a class specific table
# in other cases, separate tables are used for the properties, and a class specific table is unneeded
- if not cls.derived or any(f.is_single for f in cls.properties):
+ if not cls.derived or any(f.is_single and not f.synth for f in cls.properties):
binding = not cls.derived
keyset = KeySet(["id"]) if cls.derived else None
yield Table(
@@ -58,12 +58,15 @@ def cls_to_dbscheme(cls: schema.Class, lookup: typing.Dict[str, schema.Class], a
columns=[
Column("id", type=dbtype(cls.name), binding=binding),
] + [
- Column(f.name, dbtype(f.type, add_or_none_except)) for f in cls.properties if f.is_single
+ Column(f.name, dbtype(f.type, add_or_none_except))
+ for f in cls.properties if f.is_single and not f.synth
],
dir=dir,
)
# use property-specific tables for 1-to-many and 1-to-at-most-1 properties
for f in cls.properties:
+ if f.synth:
+ continue
if f.is_unordered:
yield Table(
name=inflection.tableize(f"{cls.name}_{f.name}"),
diff --git a/misc/codegen/test/test_dbschemegen.py b/misc/codegen/test/test_dbschemegen.py
index 86b9dd2fc84..45b5718a876 100644
--- a/misc/codegen/test/test_dbschemegen.py
+++ b/misc/codegen/test/test_dbschemegen.py
@@ -566,5 +566,32 @@ def test_ipa_derived_classes_ignored(generate):
)
+def test_synth_properties_ignored(generate):
+ assert generate([
+ schema.Class(name="A", properties=[
+ schema.SingleProperty("x", "a"),
+ schema.SingleProperty("y", "b", synth=True),
+ schema.SingleProperty("z", "c"),
+ schema.OptionalProperty("foo", "bar", synth=True),
+ schema.RepeatedProperty("baz", "bazz", synth=True),
+ schema.RepeatedOptionalProperty("bazzz", "bazzzz", synth=True),
+ schema.RepeatedUnorderedProperty("bazzzzz", "bazzzzzz", synth=True),
+ ]),
+ ]) == dbscheme.Scheme(
+ src=schema_file.name,
+ includes=[],
+ declarations=[
+ dbscheme.Table(
+ name="as",
+ columns=[
+ dbscheme.Column("id", "@a", binding=True),
+ dbscheme.Column("x", "a"),
+ dbscheme.Column("z", "c"),
+ ],
+ )
+ ],
+ )
+
+
if __name__ == '__main__':
sys.exit(pytest.main([__file__] + sys.argv[1:]))
From b09386a2c8ff10d6227a04236c688afe340a4f43 Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 15:55:57 +0200
Subject: [PATCH 112/813] Codegen: ignore `synth` properties in `Raw.qll`
---
misc/codegen/templates/ql_db.mustache | 2 ++
1 file changed, 2 insertions(+)
diff --git a/misc/codegen/templates/ql_db.mustache b/misc/codegen/templates/ql_db.mustache
index d16416f4a5b..188abebc120 100644
--- a/misc/codegen/templates/ql_db.mustache
+++ b/misc/codegen/templates/ql_db.mustache
@@ -15,6 +15,7 @@ module Raw {
{{#final}}override string toString() { result = "{{name}}" }{{/final}}
{{#properties}}
+ {{^synth}}
/**
* {{>ql_property_doc}} *
{{#has_description}}
@@ -26,6 +27,7 @@ module Raw {
{{type}} {{getter}}({{#is_indexed}}int index{{/is_indexed}}) {
{{tablename}}({{#tableparams}}{{^first}}, {{/first}}{{param}}{{/tableparams}})
}
+ {{/synth}}
{{/properties}}
}
From cc271d682e387a0163fa21de7c50ce36fecf0d91 Mon Sep 17 00:00:00 2001
From: Paolo Tranquilli
Date: Tue, 23 May 2023 16:46:43 +0200
Subject: [PATCH 113/813] Codegen: ignore `synth` properties in `cppgen`
---
misc/codegen/generators/cppgen.py | 2 +-
misc/codegen/test/test_cppgen.py | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/misc/codegen/generators/cppgen.py b/misc/codegen/generators/cppgen.py
index 1cd6cc36450..8f522dc1435 100644
--- a/misc/codegen/generators/cppgen.py
+++ b/misc/codegen/generators/cppgen.py
@@ -76,7 +76,7 @@ class Processor:
bases=[self._get_class(b) for b in cls.bases],
fields=[
_get_field(cls, p, self._add_or_none_except)
- for p in cls.properties if "cpp_skip" not in p.pragmas
+ for p in cls.properties if "cpp_skip" not in p.pragmas and not p.synth
],
final=not cls.derived,
trap_name=trap_name,
diff --git a/misc/codegen/test/test_cppgen.py b/misc/codegen/test/test_cppgen.py
index 1bc7d150b2e..ebb7b3887ef 100644
--- a/misc/codegen/test/test_cppgen.py
+++ b/misc/codegen/test/test_cppgen.py
@@ -203,5 +203,27 @@ def test_ipa_classes_ignored(generate):
]
+def test_synth_properties_ignored(generate):
+ assert generate([
+ schema.Class(
+ name="X",
+ properties=[
+ schema.SingleProperty("x", "a"),
+ schema.SingleProperty("y", "b", synth=True),
+ schema.SingleProperty("z", "c"),
+ schema.OptionalProperty("foo", "bar", synth=True),
+ schema.RepeatedProperty("baz", "bazz", synth=True),
+ schema.RepeatedOptionalProperty("bazzz", "bazzzz", synth=True),
+ schema.RepeatedUnorderedProperty("bazzzzz", "bazzzzzz", synth=True),
+ ],
+ ),
+ ]) == [
+ cpp.Class(name="X", final=True, trap_name="Xes", fields=[
+ cpp.Field("x", "a"),
+ cpp.Field("z", "c"),
+ ]),
+ ]
+
+
if __name__ == '__main__':
sys.exit(pytest.main([__file__] + sys.argv[1:]))
From 76d731a61da81fac609f485fdbf36be997f8ecc5 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 25 May 2023 16:28:07 +0200
Subject: [PATCH 114/813] improve CannotBeTaintedCharacteristic
---
.../ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll | 1 +
1 file changed, 1 insertion(+)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 736e8b947bf..c0c02ffe3d6 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -345,6 +345,7 @@ private class CannotBeTaintedCharacteristic extends CharacteristicsImpl::LikelyN
* Holds if the node `n` is known as the predecessor in a modeled flow step.
*/
private predicate isKnownOutNodeForStep(Endpoint e) {
+ e.asExpr() instanceof Call or // we just assume flow in that case
TaintTracking::localTaintStep(_, e) or
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(_, e, _) or
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(_, e, _) or
From db77c6b9a34f01922385c1947ae033b68c92f6e8 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 25 May 2023 16:39:27 +0200
Subject: [PATCH 115/813] Java: mark functional expressions as likely not sinks
---
.../AutomodelApplicationModeCharacteristics.qll | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index c0c02ffe3d6..c3d6a91946a 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -328,6 +328,17 @@ private class OtherArgumentToModeledMethodCharacteristic extends Characteristics
}
}
+/**
+ * A characteristic that marks functional expression as likely not sinks.
+ *
+ * These expressions may well _contain_ sinks, but rarely are sinks themselves.
+ */
+private class FunctionValueCharacteristic extends CharacteristicsImpl::LikelyNotASinkCharacteristic {
+ FunctionValueCharacteristic() { this = "function value" }
+
+ override predicate appliesToEndpoint(Endpoint e) { e.asExpr() instanceof FunctionalExpr }
+}
+
/**
* A negative characteristic that indicates that an endpoint is not a `to` node for any known taint step. Such a node
* cannot be tainted, because taint can't flow into it.
From 5ca22210976f0d91e8252b8c52c3237fac8d1654 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Thu, 25 May 2023 17:06:02 +0200
Subject: [PATCH 116/813] remove some of the biggest frameworks from
application mode consideration
---
...utomodelApplicationModeCharacteristics.qll | 23 +++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index c3d6a91946a..138e508b1d2 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -277,6 +277,29 @@ private class ArgumentToLocalCall extends CharacteristicsImpl::UninterestingToMo
}
}
+/**
+ * A characteristic that avoids modeling endpoint that are passed to frameworks
+ * in application mode.
+ *
+ * It's much more economical to use framework mode for those.
+ */
+private class SkipFrameworkModeling extends CharacteristicsImpl::UninterestingToModelCharacteristic {
+ SkipFrameworkModeling() { this = "skip modeling of large frameworks" }
+
+ override predicate appliesToEndpoint(Endpoint e) {
+ ApplicationCandidatesImpl::getCallable(e)
+ .getDeclaringType()
+ .getPackage()
+ .getName()
+ .matches([
+ "com.google%", //
+ "java.%", //
+ "javax.%", //
+ "org.apache%", //
+ ])
+ }
+}
+
/**
* A Characteristic that marks endpoints as uninteresting to model, according to the Java ModelExclusions module.
*/
From 85a1ab0264e62bef86b7564a7808b6d73220de66 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 25 May 2023 16:10:31 +0100
Subject: [PATCH 117/813] Swift: Undo autocorrect.
---
.../change-notes/2023-05-25-string-length-conflation-fp.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md b/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
index 937ebd4b41a..7166b5e9ed7 100644
--- a/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
+++ b/swift/ql/src/change-notes/2023-05-25-string-length-conflation-fp.md
@@ -1,4 +1,4 @@
-—
+---
category: minorAnalysis
-—
+---
* Fixed some false positive results from the `swift/string-length-conflation` query, caused by imprecise sinks.
From 0e443da7106bd86078b98e2ab3aeb09bb17ba49a Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 25 May 2023 17:43:42 +0100
Subject: [PATCH 118/813] Swift: Remove id() categorization due to accuracy,
and repair the old bank.?account case.
---
.../codeql/swift/security/SensitiveExprs.qll | 8 +-
.../CWE-311/CleartextTransmission.expected | 4 -
.../Security/CWE-311/SensitiveExprs.expected | 85 +++++++++----------
.../Security/CWE-311/testURL.swift | 2 +-
.../CWE-328/WeakSensitiveDataHashing.expected | 12 ---
.../Security/CWE-328/testCryptoKit.swift | 12 +--
6 files changed, 53 insertions(+), 70 deletions(-)
diff --git a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
index c3432fa58e4..b6f5c231823 100644
--- a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
+++ b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
@@ -30,7 +30,11 @@ class SensitiveCredential extends SensitiveDataType, TCredential {
override string toString() { result = "credential" }
override string getRegexp() {
- result = HeuristicNames::maybeSensitiveRegexp(_) or
+ exists(SensitiveDataClassification classification |
+ not classification = SensitiveDataClassification::id() and // not accurate enough
+ result = HeuristicNames::maybeSensitiveRegexp(classification)
+ )
+ or
result = "(?is).*(license.?key).*"
}
}
@@ -54,7 +58,7 @@ class SensitivePrivateInfo extends SensitiveDataType, TPrivateInfo {
// Geographic location - where the user is (or was)
"latitude|longitude|" +
// Financial data - such as credit card numbers, salary, bank accounts, and debts
- "credit.?card|debit.?card|salary|" +
+ "credit.?card|debit.?card|salary|bank.?account|" +
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
"email|" +
// Health - medical conditions, insurance status, prescription records
diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
index c832cf51a15..94272faf6d0 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
+++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
@@ -13,7 +13,6 @@ edges
| testSend.swift:54:17:54:17 | password | testSend.swift:41:10:41:18 | data |
| testSend.swift:54:17:54:17 | password | testSend.swift:54:13:54:25 | call to pad(_:) |
| testURL.swift:13:54:13:54 | passwd | testURL.swift:13:22:13:54 | ... .+(_:_:) ... |
-| testURL.swift:15:55:15:55 | account_no | testURL.swift:15:22:15:55 | ... .+(_:_:) ... |
| testURL.swift:16:55:16:55 | credit_card_no | testURL.swift:16:22:16:55 | ... .+(_:_:) ... |
nodes
| file://:0:0:0:0 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) |
@@ -41,8 +40,6 @@ nodes
| testSend.swift:66:27:66:30 | .mobileNumber | semmle.label | .mobileNumber |
| testURL.swift:13:22:13:54 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| testURL.swift:13:54:13:54 | passwd | semmle.label | passwd |
-| testURL.swift:15:22:15:55 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
-| testURL.swift:15:55:15:55 | account_no | semmle.label | account_no |
| testURL.swift:16:22:16:55 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| testURL.swift:16:55:16:55 | credit_card_no | semmle.label | credit_card_no |
| testURL.swift:20:22:20:22 | passwd | semmle.label | passwd |
@@ -61,6 +58,5 @@ subpaths
| testSend.swift:65:27:65:27 | license_key | testSend.swift:65:27:65:27 | license_key | testSend.swift:65:27:65:27 | license_key | This operation transmits 'license_key', which may contain unencrypted sensitive data from $@. | testSend.swift:65:27:65:27 | license_key | license_key |
| testSend.swift:66:27:66:30 | .mobileNumber | testSend.swift:66:27:66:30 | .mobileNumber | testSend.swift:66:27:66:30 | .mobileNumber | This operation transmits '.mobileNumber', which may contain unencrypted sensitive data from $@. | testSend.swift:66:27:66:30 | .mobileNumber | .mobileNumber |
| testURL.swift:13:22:13:54 | ... .+(_:_:) ... | testURL.swift:13:54:13:54 | passwd | testURL.swift:13:22:13:54 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:13:54:13:54 | passwd | passwd |
-| testURL.swift:15:22:15:55 | ... .+(_:_:) ... | testURL.swift:15:55:15:55 | account_no | testURL.swift:15:22:15:55 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:15:55:15:55 | account_no | account_no |
| testURL.swift:16:22:16:55 | ... .+(_:_:) ... | testURL.swift:16:55:16:55 | credit_card_no | testURL.swift:16:22:16:55 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:16:55:16:55 | credit_card_no | credit_card_no |
| testURL.swift:20:22:20:22 | passwd | testURL.swift:20:22:20:22 | passwd | testURL.swift:20:22:20:22 | passwd | This operation transmits 'passwd', which may contain unencrypted sensitive data from $@. | testURL.swift:20:22:20:22 | passwd | passwd |
diff --git a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
index 708d68a05e9..89b8d36ccdf 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
+++ b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
@@ -4,54 +4,50 @@
| testAlamofire.swift:159:26:159:26 | email | label:email, type:private information |
| testAlamofire.swift:171:35:171:35 | email | label:email, type:private information |
| testAlamofire.swift:177:35:177:35 | email | label:email, type:private information |
-| testAlamofire.swift:187:48:187:48 | username | label:username, type:credential |
| testAlamofire.swift:187:65:187:65 | password | label:password, type:credential |
-| testAlamofire.swift:195:47:195:47 | username | label:username, type:credential |
| testAlamofire.swift:195:64:195:64 | password | label:password, type:credential |
-| testAlamofire.swift:205:45:205:45 | username | label:username, type:credential |
| testAlamofire.swift:205:62:205:62 | password | label:password, type:credential |
-| testAlamofire.swift:213:48:213:48 | username | label:username, type:credential |
| testAlamofire.swift:213:65:213:65 | password | label:password, type:credential |
-| testCoreData2.swift:37:16:37:16 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:38:2:38:6 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:39:2:39:6 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:39:28:39:28 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:40:2:40:6 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:credential |
-| testCoreData2.swift:41:2:41:6 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:credential |
-| testCoreData2.swift:41:29:41:29 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:42:2:42:6 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:credential |
-| testCoreData2.swift:43:2:43:6 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:credential |
-| testCoreData2.swift:43:35:43:35 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:46:22:46:22 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:47:2:47:12 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:48:2:48:12 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:48:34:48:34 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:49:2:49:12 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:credential |
-| testCoreData2.swift:50:2:50:12 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:credential |
-| testCoreData2.swift:50:35:50:35 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:51:2:51:12 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:credential |
-| testCoreData2.swift:52:2:52:12 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:credential |
-| testCoreData2.swift:52:41:52:41 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:57:3:57:7 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:57:29:57:29 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:60:4:60:8 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:60:30:60:30 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:62:4:62:8 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:62:30:62:30 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:65:3:65:7 | .myBankAccountNumber | label:myBankAccountNumber, type:credential |
-| testCoreData2.swift:65:29:65:29 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:79:18:79:28 | .bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | label:bankAccountNo2, type:credential |
-| testCoreData2.swift:82:18:82:18 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:83:18:83:18 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:84:18:84:18 | bankAccountNo2 | label:bankAccountNo2, type:credential |
-| testCoreData2.swift:85:18:85:18 | bankAccountNo2 | label:bankAccountNo2, type:credential |
-| testCoreData2.swift:87:22:87:32 | .bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:88:22:88:22 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:89:22:89:22 | bankAccountNo2 | label:bankAccountNo2, type:credential |
-| testCoreData2.swift:91:10:91:10 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:95:10:95:10 | bankAccountNo | label:bankAccountNo, type:credential |
-| testCoreData2.swift:101:10:101:10 | bankAccountNo | label:bankAccountNo, type:credential |
+| testCoreData2.swift:37:16:37:16 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:38:2:38:6 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:39:2:39:6 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:39:28:39:28 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:40:2:40:6 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:private information |
+| testCoreData2.swift:41:2:41:6 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:private information |
+| testCoreData2.swift:41:29:41:29 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:42:2:42:6 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:private information |
+| testCoreData2.swift:43:2:43:6 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:private information |
+| testCoreData2.swift:43:35:43:35 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:46:22:46:22 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:47:2:47:12 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:48:2:48:12 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:48:34:48:34 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:49:2:49:12 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:private information |
+| testCoreData2.swift:50:2:50:12 | .myBankAccountNumber2 | label:myBankAccountNumber2, type:private information |
+| testCoreData2.swift:50:35:50:35 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:51:2:51:12 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:private information |
+| testCoreData2.swift:52:2:52:12 | .notStoredBankAccountNumber | label:notStoredBankAccountNumber, type:private information |
+| testCoreData2.swift:52:41:52:41 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:57:3:57:7 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:57:29:57:29 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:60:4:60:8 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:60:30:60:30 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:62:4:62:8 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:62:30:62:30 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:65:3:65:7 | .myBankAccountNumber | label:myBankAccountNumber, type:private information |
+| testCoreData2.swift:65:29:65:29 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:79:18:79:28 | .bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:80:18:80:28 | .bankAccountNo2 | label:bankAccountNo2, type:private information |
+| testCoreData2.swift:82:18:82:18 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:83:18:83:18 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:84:18:84:18 | bankAccountNo2 | label:bankAccountNo2, type:private information |
+| testCoreData2.swift:85:18:85:18 | bankAccountNo2 | label:bankAccountNo2, type:private information |
+| testCoreData2.swift:87:22:87:32 | .bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:88:22:88:22 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:89:22:89:22 | bankAccountNo2 | label:bankAccountNo2, type:private information |
+| testCoreData2.swift:91:10:91:10 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:95:10:95:10 | bankAccountNo | label:bankAccountNo, type:private information |
+| testCoreData2.swift:101:10:101:10 | bankAccountNo | label:bankAccountNo, type:private information |
| testCoreData.swift:48:15:48:15 | password | label:password, type:credential |
| testCoreData.swift:51:24:51:24 | password | label:password, type:credential |
| testCoreData.swift:58:15:58:15 | password | label:password, type:credential |
@@ -133,6 +129,5 @@
| testSend.swift:66:27:66:30 | .mobileNumber | label:mobileNumber, type:private information |
| testSend.swift:69:27:69:30 | .passwordFeatureEnabled | label:passwordFeatureEnabled, type:credential |
| testURL.swift:13:54:13:54 | passwd | label:passwd, type:credential |
-| testURL.swift:15:55:15:55 | account_no | label:account_no, type:credential |
| testURL.swift:16:55:16:55 | credit_card_no | label:credit_card_no, type:private information |
| testURL.swift:20:22:20:22 | passwd | label:passwd, type:credential |
diff --git a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
index 7f9b64ff4f6..48ae815232f 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
+++ b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
@@ -12,7 +12,7 @@ struct URL
func test1(passwd : String, encrypted_passwd : String, account_no : String, credit_card_no : String) {
let a = URL(string: "http://example.com/login?p=" + passwd); // BAD
let b = URL(string: "http://example.com/login?p=" + encrypted_passwd); // GOOD (not sensitive)
- let c = URL(string: "http://example.com/login?ac=" + account_no); // BAD
+ let c = URL(string: "http://example.com/login?ac=" + account_no); // BAD [NOT DETECTED]
let d = URL(string: "http://example.com/login?cc=" + credit_card_no); // BAD
let base = URL(string: "http://example.com/"); // GOOD (not sensitive)
diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
index 16fc67d4a6f..b6c7161b853 100644
--- a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
+++ b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
@@ -2,28 +2,22 @@ edges
nodes
| testCryptoKit.swift:56:47:56:47 | passwd | semmle.label | passwd |
| testCryptoKit.swift:57:43:57:43 | cert | semmle.label | cert |
-| testCryptoKit.swift:59:43:59:43 | account_no | semmle.label | account_no |
| testCryptoKit.swift:60:43:60:43 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:61:43:61:43 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:63:44:63:44 | passwd | semmle.label | passwd |
| testCryptoKit.swift:64:44:64:44 | cert | semmle.label | cert |
-| testCryptoKit.swift:66:44:66:44 | account_no | semmle.label | account_no |
| testCryptoKit.swift:67:44:67:44 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:90:23:90:23 | passwd | semmle.label | passwd |
| testCryptoKit.swift:91:23:91:23 | cert | semmle.label | cert |
-| testCryptoKit.swift:93:23:93:23 | account_no | semmle.label | account_no |
| testCryptoKit.swift:94:23:94:23 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:99:23:99:23 | passwd | semmle.label | passwd |
| testCryptoKit.swift:100:23:100:23 | cert | semmle.label | cert |
-| testCryptoKit.swift:102:23:102:23 | account_no | semmle.label | account_no |
| testCryptoKit.swift:103:23:103:23 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:132:32:132:32 | passwd | semmle.label | passwd |
| testCryptoKit.swift:133:32:133:32 | cert | semmle.label | cert |
-| testCryptoKit.swift:135:32:135:32 | account_no | semmle.label | account_no |
| testCryptoKit.swift:136:32:136:32 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:141:32:141:32 | passwd | semmle.label | passwd |
| testCryptoKit.swift:142:32:142:32 | cert | semmle.label | cert |
-| testCryptoKit.swift:144:32:144:32 | account_no | semmle.label | account_no |
| testCryptoKit.swift:145:32:145:32 | credit_card_no | semmle.label | credit_card_no |
| testCryptoSwift.swift:113:30:113:30 | passwdArray | semmle.label | passwdArray |
| testCryptoSwift.swift:115:31:115:31 | passwdArray | semmle.label | passwdArray |
@@ -39,28 +33,22 @@ subpaths
#select
| testCryptoKit.swift:56:47:56:47 | passwd | testCryptoKit.swift:56:47:56:47 | passwd | testCryptoKit.swift:56:47:56:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:56:47:56:47 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:57:43:57:43 | cert | testCryptoKit.swift:57:43:57:43 | cert | testCryptoKit.swift:57:43:57:43 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:57:43:57:43 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:59:43:59:43 | account_no | testCryptoKit.swift:59:43:59:43 | account_no | testCryptoKit.swift:59:43:59:43 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:59:43:59:43 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:60:43:60:43 | credit_card_no | testCryptoKit.swift:60:43:60:43 | credit_card_no | testCryptoKit.swift:60:43:60:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:60:43:60:43 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:61:43:61:43 | credit_card_no | testCryptoKit.swift:61:43:61:43 | credit_card_no | testCryptoKit.swift:61:43:61:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:61:43:61:43 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:63:44:63:44 | passwd | testCryptoKit.swift:63:44:63:44 | passwd | testCryptoKit.swift:63:44:63:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:63:44:63:44 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:64:44:64:44 | cert | testCryptoKit.swift:64:44:64:44 | cert | testCryptoKit.swift:64:44:64:44 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:64:44:64:44 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:66:44:66:44 | account_no | testCryptoKit.swift:66:44:66:44 | account_no | testCryptoKit.swift:66:44:66:44 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:66:44:66:44 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:67:44:67:44 | credit_card_no | testCryptoKit.swift:67:44:67:44 | credit_card_no | testCryptoKit.swift:67:44:67:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:67:44:67:44 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:90:23:90:23 | passwd | testCryptoKit.swift:90:23:90:23 | passwd | testCryptoKit.swift:90:23:90:23 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:90:23:90:23 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:91:23:91:23 | cert | testCryptoKit.swift:91:23:91:23 | cert | testCryptoKit.swift:91:23:91:23 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:91:23:91:23 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:93:23:93:23 | account_no | testCryptoKit.swift:93:23:93:23 | account_no | testCryptoKit.swift:93:23:93:23 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:93:23:93:23 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:94:23:94:23 | credit_card_no | testCryptoKit.swift:94:23:94:23 | credit_card_no | testCryptoKit.swift:94:23:94:23 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:94:23:94:23 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:99:23:99:23 | passwd | testCryptoKit.swift:99:23:99:23 | passwd | testCryptoKit.swift:99:23:99:23 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:99:23:99:23 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:100:23:100:23 | cert | testCryptoKit.swift:100:23:100:23 | cert | testCryptoKit.swift:100:23:100:23 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:100:23:100:23 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:102:23:102:23 | account_no | testCryptoKit.swift:102:23:102:23 | account_no | testCryptoKit.swift:102:23:102:23 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:102:23:102:23 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:103:23:103:23 | credit_card_no | testCryptoKit.swift:103:23:103:23 | credit_card_no | testCryptoKit.swift:103:23:103:23 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:103:23:103:23 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:132:32:132:32 | passwd | testCryptoKit.swift:132:32:132:32 | passwd | testCryptoKit.swift:132:32:132:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:132:32:132:32 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:133:32:133:32 | cert | testCryptoKit.swift:133:32:133:32 | cert | testCryptoKit.swift:133:32:133:32 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:133:32:133:32 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:135:32:135:32 | account_no | testCryptoKit.swift:135:32:135:32 | account_no | testCryptoKit.swift:135:32:135:32 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:135:32:135:32 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:136:32:136:32 | credit_card_no | testCryptoKit.swift:136:32:136:32 | credit_card_no | testCryptoKit.swift:136:32:136:32 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:136:32:136:32 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:141:32:141:32 | passwd | testCryptoKit.swift:141:32:141:32 | passwd | testCryptoKit.swift:141:32:141:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:141:32:141:32 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:142:32:142:32 | cert | testCryptoKit.swift:142:32:142:32 | cert | testCryptoKit.swift:142:32:142:32 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:142:32:142:32 | cert | sensitive data (credential cert) |
-| testCryptoKit.swift:144:32:144:32 | account_no | testCryptoKit.swift:144:32:144:32 | account_no | testCryptoKit.swift:144:32:144:32 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:144:32:144:32 | account_no | sensitive data (credential account_no) |
| testCryptoKit.swift:145:32:145:32 | credit_card_no | testCryptoKit.swift:145:32:145:32 | credit_card_no | testCryptoKit.swift:145:32:145:32 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:145:32:145:32 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoSwift.swift:113:30:113:30 | passwdArray | testCryptoSwift.swift:113:30:113:30 | passwdArray | testCryptoSwift.swift:113:30:113:30 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:113:30:113:30 | passwdArray | sensitive data (credential passwdArray) |
| testCryptoSwift.swift:115:31:115:31 | passwdArray | testCryptoSwift.swift:115:31:115:31 | passwdArray | testCryptoSwift.swift:115:31:115:31 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:115:31:115:31 | passwdArray | sensitive data (credential passwdArray) |
diff --git a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
index 55fb9284fa6..4e6f301c84b 100644
--- a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
+++ b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
@@ -56,14 +56,14 @@ func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_pa
var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD
hash = Crypto.Insecure.MD5.hash(data: cert) // BAD
hash = Crypto.Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive)
- hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD
+ hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD [NOT DETECTED]
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD
hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD
hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD
hash = Crypto.Insecure.SHA1.hash(data: encrypted_passwd) // GOOD (not sensitive)
- hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD
+ hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD [NOT DETECTED]
hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD
hash = Crypto.SHA256.hash(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash
@@ -90,7 +90,7 @@ func testMD5UpdateWithData(passwd : String, cert: String, encrypted_passwd : Str
hash.update(data: passwd) // BAD
hash.update(data: cert) // BAD
hash.update(data: encrypted_passwd) // GOOD (not sensitive)
- hash.update(data: account_no) // BAD
+ hash.update(data: account_no) // BAD [NOT DETECTED]
hash.update(data: credit_card_no) // BAD
}
@@ -99,7 +99,7 @@ func testSHA1UpdateWithData(passwd : String, cert: String, encrypted_passwd : St
hash.update(data: passwd) // BAD
hash.update(data: cert) // BAD
hash.update(data: encrypted_passwd) // GOOD (not sensitive)
- hash.update(data: account_no) // BAD
+ hash.update(data: account_no) // BAD [NOT DETECTED]
hash.update(data: credit_card_no) // BAD
}
@@ -132,7 +132,7 @@ func testMD5UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, ce
hash.update(bufferPointer: passwd) // BAD
hash.update(bufferPointer: cert) // BAD
hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive)
- hash.update(bufferPointer: account_no) // BAD
+ hash.update(bufferPointer: account_no) // BAD [NOT DETECTED]
hash.update(bufferPointer: credit_card_no) // BAD
}
@@ -141,7 +141,7 @@ func testSHA1UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, c
hash.update(bufferPointer: passwd) // BAD
hash.update(bufferPointer: cert) // BAD
hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive)
- hash.update(bufferPointer: account_no) // BAD
+ hash.update(bufferPointer: account_no) // BAD [NOT DETECTED]
hash.update(bufferPointer: credit_card_no) // BAD
}
From 0d1d20c75b9c088fa5dea8d50565a2026485af2e Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Thu, 25 May 2023 15:50:29 -0700
Subject: [PATCH 119/813] C++: Change range-analysis test to not use 'getAst'.
This was creating confusing test expectation annotations.
---
.../ir/range-analysis/RangeAnalysis.ql | 9 +-
.../SimpleRangeAnalysis_tests.cpp | 114 +++++++++---------
.../library-tests/ir/range-analysis/test.cpp | 12 +-
3 files changed, 64 insertions(+), 71 deletions(-)
diff --git a/cpp/ql/test/library-tests/ir/range-analysis/RangeAnalysis.ql b/cpp/ql/test/library-tests/ir/range-analysis/RangeAnalysis.ql
index 84437827d0d..eadf0b90ef5 100644
--- a/cpp/ql/test/library-tests/ir/range-analysis/RangeAnalysis.ql
+++ b/cpp/ql/test/library-tests/ir/range-analysis/RangeAnalysis.ql
@@ -40,14 +40,7 @@ bindingset[delta]
private string getBoundString(SemBound b, float delta) {
b instanceof SemZeroBound and result = delta.toString()
or
- result =
- strictconcat(b.(SemSsaBound)
- .getAVariable()
- .(SemanticExprConfig::SsaVariable)
- .asInstruction()
- .getAst()
- .toString(), ":"
- ) + getOffsetString(delta)
+ result = strictconcat(b.(SemSsaBound).getAVariable().toString(), ":") + getOffsetString(delta)
}
private string getARangeString(SemExpr e) {
diff --git a/cpp/ql/test/library-tests/ir/range-analysis/SimpleRangeAnalysis_tests.cpp b/cpp/ql/test/library-tests/ir/range-analysis/SimpleRangeAnalysis_tests.cpp
index eed0a7d7e47..92e197115b7 100644
--- a/cpp/ql/test/library-tests/ir/range-analysis/SimpleRangeAnalysis_tests.cpp
+++ b/cpp/ql/test/library-tests/ir/range-analysis/SimpleRangeAnalysis_tests.cpp
@@ -8,7 +8,7 @@ int test1(struct List* p) {
int count = 0;
for (; p; p = p->next) {
count = count+1;
- range(count); // $ range===count:p+1
+ range(count); // $ range="==Phi: p:Store: count+1"
}
range(count);
return count;
@@ -18,7 +18,7 @@ int test2(struct List* p) {
int count = 0;
for (; p; p = p->next) {
count = (count+1) % 10;
- range(count); // $ range=<=9 range=>=-9 range=<=count:p+1
+ range(count); // $ range=<=9 range=>=-9 range="<=Phi: p:Store: count+1"
}
range(count); // $ range=>=-9 range=<=9
return count;
@@ -29,7 +29,7 @@ int test3(struct List* p) {
for (; p; p = p->next) {
range(count++); // $ range=>=-9 range=<=9
count = count % 10;
- range(count); // $ range=<=9 range=>=-9 range="<=... +++0" range=<=count:p+1
+ range(count); // $ range=<=9 range=>=-9 range="<=Store: ... +++0" range="<=Phi: p:Store: count+1"
}
range(count); // $ range=>=-9 range=<=9
return count;
@@ -42,11 +42,11 @@ int test4() {
range(i); // $ range=<=1 range=>=0
range(total);
total += i;
- range(total); // $ range=<=i+1 range=<=i+1 MISSING: range=>=0 range=>=i+0
+ range(total); // $ range="<=Phi: i+1" MISSING: range=>=0 range=>=i+0
}
range(total); // $ MISSING: range=>=0
range(i); // $ range===2
- range(total + i); // $ range=<=i+2 MISSING: range===i+2 range=>=2 range=>=i+0
+ range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
return total + i;
}
@@ -57,11 +57,11 @@ int test5() {
range(i); // $ range=<=1 range=>=0
range(total); // $ MISSING: range=>=0
total += i;
- range(total); // $ range=<=i+1 MISSING: range=>=0 range=>=i+0
+ range(total); // $ range="<=Phi: i+1" MISSING: range=>=0 range=>=i+0
}
range(total); // $ MISSING: range=>=0
range(i); // $ range===2
- range(total + i); // $ range=<=i+2 MISSING: range===i+2 range=>=2 range=>=i+0
+ range(total + i); // $ range="<=Phi: i+2" MISSING: range===i+2 range=>=2 range=>=i+0
return total + i;
}
@@ -72,7 +72,7 @@ int test6() {
range(i); // $ range=<=1 range=>=0
range(total); // $ MISSING: range=>=0
total += i;
- range(total); // $ range=<=i+1 MISSING: range=>=0 range=>=i+0
+ range(total); // $ range="<=Phi: i+1" MISSING: range=>=0 range=>=i+0
}
return total + i;
}
@@ -93,12 +93,12 @@ int test8(int x, int y) {
if (-1000 < y && y < 10) {
range(y); // $ range=<=9 range=>=-999
if (x < y-2) {
- range(x); // $ range=<=6 range=<=y-3
- range(y); // $ range=<=9 range=>=-999 range=>=x+3
+ range(x); // $ range=<=6 range="<=InitializeParameter: y:Store: y-3"
+ range(y); // $ range=<=9 range=>=-999 range=">=InitializeParameter: x:Store: x+3"
return x;
}
- range(x); // $ range=>=-1001 range=>=y-2
- range(y); // $ range=<=9 range=<=x+2 range=>=-999
+ range(x); // $ range=>=-1001 range=">=InitializeParameter: y:Store: y-2"
+ range(y); // $ range=<=9 range="<=InitializeParameter: x:Store: x+2" range=>=-999
}
range(x);
range(y);
@@ -127,12 +127,12 @@ int test10(int x, int y) {
if (y > 7) {
range(y); // $ range=>=8
if (x < y) {
- range(x); // $ range=<=y-1
- range(y); // $ range=>=8 range=>=x+1
+ range(x); // $ range="<=InitializeParameter: y-1"
+ range(y); // $ range=>=8 range=">=InitializeParameter: x:Store: x+1"
return 0;
}
- range(x); // $ range=>=8 range=>=y+0
- range(y); // $ range=<=x+0 range=>=8
+ range(x); // $ range=>=8 range=">=InitializeParameter: y+0"
+ range(y); // $ range="<=InitializeParameter: x:Store: x+0" range=>=8
return x;
}
range(y); // $ range=<=7
@@ -145,7 +145,7 @@ int test11(char *p) {
range(*p);
if (c != '\0') {
*p++ = '\0';
- range(p); // $ range===p+1
+ range(p); // $ range="==InitializeParameter: p+1"
range(*p);
}
if (c == ':') {
@@ -155,7 +155,7 @@ int test11(char *p) {
if (c != '\0') {
range(c);
*p++ = '\0';
- range(p); // $ range=<=p+2 range===c+1 range=>=p+1
+ range(p); // $ range="<=InitializeParameter: p+2" range="==Phi: c+1" range=">=InitializeParameter: p+1"
}
if (c != ',') {
return 1;
@@ -193,7 +193,7 @@ int test13(char c, int i) {
unsigned int y = x-1; // $ overflow=-
range(y); // $ range===-1 overflow=-
int z = i+1; // $ overflow=+
- range(z); // $ range===i+1
+ range(z); // $ range="==InitializeParameter: i+1"
range(c + i + uc + x + y + z); // $ overflow=+- overflow=+ overflow=- MISSING: range=>=1
range((double)(c + i + uc + x + y + z)); // $ overflow=+ overflow=+- overflow=- MISSING: range=>=1
return (double)(c + i + uc + x + y + z); // $ overflow=+- overflow=+ overflow=-
@@ -245,7 +245,7 @@ int test_unary(int a) {
range(c); // $ range=<=0 range=>=-11
range(b+c); // $ range=<=11 range=>=-11 MISSING:range=">=- ...+0"
total += b+c;
- range(total); // $ range=<=0+11 range=<=19 range=>=0-11 range=>=-19
+ range(total); // $ range="<=Phi: 0+11" range=<=19 range=">=Phi: 0-11" range=>=-19
}
if (-7 <= a && a <= 11) {
range(a); // $ range=<=11 range=>=-7
@@ -255,7 +255,7 @@ int test_unary(int a) {
range(c); // $ range=<=7 range=>=-11
range(b+c); // $ range=<=18 range=>=-18
total += b+c;
- range(total); // $ range="<=- ...+18" range=">=- ...-18" range=<=0+29 range=<=37 range=>=0-29 range=>=-37
+ range(total); // $ range="<=Phi: - ...+18" range=">=Phi: - ...-18" range="<=Phi: 0+29" range=<=37 range=">=Phi: 0-29" range=>=-37
}
if (-7 <= a && a <= 1) {
range(a); // $ range=<=1 range=>=-7
@@ -265,7 +265,7 @@ int test_unary(int a) {
range(c); // $ range=<=7 range=>=-1
range(b+c); // $ range=<=8 range=>=-8
total += b+c;
- range(total); // $ range="<=- ...+8" range="<=- ...+26" range=">=- ...-8" range=">=- ...-26" range=<=0+37 range=<=45 range=>=0-37 range=>=-45
+ range(total); // $ range="<=Phi: - ...+8" range="<=Phi: - ...+26" range=">=Phi: - ...-8" range=">=Phi: - ...-26" range="<=Phi: 0+37" range=<=45 range=">=Phi: 0-37" range=>=-45
}
if (-7 <= a && a <= 0) {
range(a); // $ range=<=0 range=>=-7
@@ -275,7 +275,7 @@ int test_unary(int a) {
range(c); // $ range=<=7 range=>=0
range(b+c); // $ range=>=-7 range=<=7 MISSING:range="<=- ...+0"
total += b+c;
- range(total); // $ range="<=- ...+7" range="<=- ...+15" range="<=- ...+33" range=">=- ...-7" range=">=- ...-15" range=">=- ...-33" range=<=0+44 range=<=52 range=>=0-44 range=>=-52
+ range(total); // $ range="<=Phi: - ...+7" range="<=Phi: - ...+15" range="<=Phi: - ...+33" range=">=Phi: - ...-7" range=">=Phi: - ...-15" range=">=Phi: - ...-33" range="<=Phi: 0+44" range=<=52 Unexpected result: range=">=Phi: 0-44" range=>=-52
}
if (-7 <= a && a <= -2) {
range(a); // $ range=<=-2 range=>=-7
@@ -285,9 +285,9 @@ int test_unary(int a) {
range(c); // $ range=<=7 range=>=2
range(b+c); // $ range=<=5 range=>=-5
total += b+c;
- range(total); // $ range="<=- ...+5" range="<=- ...+12" range="<=- ...+20" range="<=- ...+38" range=">=- ...-5" range=">=- ...-12" range=">=- ...-20" range=">=- ...-38" range=<=0+49 range=<=57 range=>=0-49 range=>=-57
+ range(total); // $ range="<=Phi: - ...+5" range="<=Phi: - ...+12" range="<=Phi: - ...+20" range="<=Phi: - ...+38" range=">=Phi: - ...-5" range=">=Phi: - ...-12" range=">=Phi: - ...-20" range=">=Phi: - ...-38" range="<=Phi: 0+49" range=<=57 range=">=Phi: 0-49" range=>=-57
}
- range(total); // $ range="<=- ...+5" range="<=- ...+12" range="<=- ...+20" range="<=- ...+38" range=">=- ...-5" range=">=- ...-12" range=">=- ...-20" range=">=- ...-38" range=<=0+49 range=<=57 range=>=0-49 range=>=-57
+ range(total); // $ range="<=Phi: - ...+5" range="<=Phi: - ...+12" range="<=Phi: - ...+20" range="<=Phi: - ...+38" range=">=Phi: - ...-5" range=">=Phi: - ...-12" range=">=Phi: - ...-20" range=">=Phi: - ...-38" range="<=Phi: 0+49" range=<=57 range=">=Phi: 0-49" range=>=-57
return total;
}
@@ -310,7 +310,7 @@ int test_mult01(int a, int b) {
int r = a*b; // 0 .. 253
range(r); // $ range=<=253 range=>=0
total += r;
- range(total); // $ range=<=3+253 range=<=506 range=>=0 range=>=3+0
+ range(total); // $ range="<=Phi: 3+253" range=<=506 range=>=0 range=">=Phi: 3+0"
}
if (3 <= a && a <= 11 && -13 <= b && b <= 23) {
range(a); // $ range=<=11 range=>=3
@@ -326,7 +326,7 @@ int test_mult01(int a, int b) {
int r = a*b; // -143 .. 0
range(r); // $ range=<=0 range=>=-143
total += r;
- range(total); // $ range=>=3-143
+ range(total); // $ range=">=Phi: 3-143"
}
if (3 <= a && a <= 11 && -13 <= b && b <= -7) {
range(a); // $ range=<=11 range=>=3
@@ -334,9 +334,9 @@ int test_mult01(int a, int b) {
int r = a*b; // -143 .. -21
range(r); // $ range=<=-21 range=>=-143
total += r;
- range(total); // $ range=>=3-143 range=>=3-286
+ range(total); // $ range=">=Phi: 3-143" range=">=Phi: 3-286"
}
- range(total); // $ range=>=3-143 range=>=3-286
+ range(total); // $ range=">=Phi: 3-143" range=">=Phi: 3-286"
return total;
}
@@ -358,7 +358,7 @@ int test_mult02(int a, int b) {
int r = a*b; // 0 .. 253
range(r); // $ range=<=253 range=>=0
total += r;
- range(total); // $ range=>=0 range=>=0+0 range=<=0+253 range=<=506
+ range(total); // $ range=>=0 range=">=Phi: 0+0" range="<=Phi: 0+253" range=<=506
}
if (0 <= a && a <= 11 && -13 <= b && b <= 23) {
range(a); // $ range=<=11 range=>=0
@@ -374,7 +374,7 @@ int test_mult02(int a, int b) {
int r = a*b; // -143 .. 0
range(r); // $ range=<=0 range=>=-143
total += r;
- range(total); // $ range=>=0-143
+ range(total); // $ range=">=Phi: 0-143"
}
if (0 <= a && a <= 11 && -13 <= b && b <= -7) {
range(a); // $ range=<=11 range=>=0
@@ -382,9 +382,9 @@ int test_mult02(int a, int b) {
int r = a*b; // -143 .. 0
range(r); // $ range=<=0 range=>=-143
total += r;
- range(total); // $ range=>=0-143 range=>=0-286
+ range(total); // $ range=">=Phi: 0-143" range=">=Phi: 0-286"
}
- range(total); // $range=>=0-143 range=>=0-286
+ range(total); // $range=">=Phi: 0-143" range=">=Phi: 0-286"
return total;
}
@@ -453,7 +453,7 @@ int test_mult04(int a, int b) {
int r = a*b; // -391 .. 0
range(r); // $ range=<=0 range=>=-391
total += r;
- range(total); // $ range="<=- ...+0" range=<=0 range=">=- ...-391" range=>=-782
+ range(total); // $ range="<=Phi: - ...+0" range=<=0 range=">=Phi: - ...-391" range=>=-782
}
if (-17 <= a && a <= 0 && -13 <= b && b <= 23) {
range(a); // $ range=<=0 range=>=-17
@@ -469,7 +469,7 @@ int test_mult04(int a, int b) {
int r = a*b; // 0 .. 221
range(r); // $ range=<=221 range=>=0
total += r;
- range(total); // $ range="<=- ...+221"
+ range(total); // $ range="<=Phi: - ...+221"
}
if (-17 <= a && a <= 0 && -13 <= b && b <= -7) {
range(a); // $ range=<=0 range=>=-17
@@ -477,9 +477,9 @@ int test_mult04(int a, int b) {
int r = a*b; // 0 .. 221
range(r); // $ range=<=221 range=>=0
total += r;
- range(total); // $ range="<=- ...+221" range="<=- ...+442"
+ range(total); // $ range="<=Phi: - ...+221" range="<=Phi: - ...+442"
}
- range(total); // $ range="<=- ...+221" range="<=- ...+442"
+ range(total); // $ range="<=Phi: - ...+221" range="<=Phi: - ...+442"
return total;
}
@@ -501,7 +501,7 @@ int test_mult05(int a, int b) {
int r = a*b; // -391 .. 0
range(r); // $ range=<=0 range=>=-391
total += r;
- range(total); // $ range="<=- ...+0" range=<=0 range=">=- ...-391" range=>=-782
+ range(total); // $ range="<=Phi: - ...+0" range=<=0 range=">=Phi: - ...-391" range=>=-782
}
if (-17 <= a && a <= -2 && -13 <= b && b <= 23) {
range(a); // $ range=<=-2 range=>=-17
@@ -517,7 +517,7 @@ int test_mult05(int a, int b) {
int r = a*b; // 0 .. 221
range(r); // $ range=<=221 range=>=0
total += r;
- range(total); // $ range="<=- ...+221"
+ range(total); // $ range="<=Phi: - ...+221"
}
if (-17 <= a && a <= -2 && -13 <= b && b <= -7) {
range(a); // $ range=<=-2 range=>=-17
@@ -525,9 +525,9 @@ int test_mult05(int a, int b) {
int r = a*b; // 14 .. 221
range(r); // $ range=<=221 range=>=14
total += r;
- range(total); // $ range="<=- ...+221" range="<=- ...+442"
+ range(total); // $ range="<=Phi: - ...+221" range="<=Phi: - ...+442"
}
- range(total); // $ range="<=- ...+221" range="<=- ...+442"
+ range(total); // $ range="<=Phi: - ...+221" range="<=Phi: - ...+442"
return total;
}
@@ -541,7 +541,7 @@ int test16(int x) {
while (i < 3) {
range(i); // $ range=<=2 range=>=0
i++;
- range(i); // $ range=<=3 range=>=1 range="==... = ...:i+1" SPURIOUS:range="==... = ...:i+1"
+ range(i); // $ range=<=3 range=>=1 range="==Phi: i:Store: ... = ...+1"
}
range(d);
d = i;
@@ -640,14 +640,14 @@ unsigned int test_comma01(unsigned int x) {
unsigned int y1;
unsigned int y2;
y1 = (++y, y);
- range(y1); // $ range=<=101 range="==... ? ... : ...+1"
+ range(y1); // $ range=<=101 range="==Phi: ... ? ... : ...:Store: ... ? ... : ...+1"
y2 = (y++,
- range(y), // $ range=<=102 range="==++ ...:... = ...+1" range="==... ? ... : ...+2"
+ range(y), // $ range=<=102 range="==Store: ++ ...:Store: ... = ...+1" range="==Phi: ... ? ... : ...:Store: ... ? ... : ...+2"
y += 3,
- range(y), // $ range=<=105 range="==++ ...:... = ...+4" range="==... +++3" range="==... ? ... : ...+5"
+ range(y), // $ range=<=105 range="==Store: ++ ...:Store: ... = ...+4" range="==Store: ... +++3" range="==Phi: ... ? ... : ...:Store: ... ? ... : ...+5"
y);
- range(y2); // $ range=<=105 range="==++ ...:... = ...+4" range="==... +++3" range="==... ? ... : ...+5"
- range(y1 + y2); // $ range=<=206 range="<=... ? ... : ...+106" MISSING: range=">=++ ...:... = ...+5" range=">=... +++4" range=">=... += ...:... = ...+1" range=">=... ? ... : ...+6"
+ range(y2); // $ range=<=105 range="==Store: ++ ...:Store: ... = ...+4" range="==Store: ... +++3" Unexpected result: range="==Phi: ... ? ... : ...:Store: ... ? ... : ...+5"
+ range(y1 + y2); // $ range=<=206 range="<=Phi: ... ? ... : ...:Store: ... ? ... : ...+106" MISSING: range=">=++ ...:... = ...+5" range=">=... +++4" range=">=... += ...:... = ...+1" range=">=... ? ... : ...+6"
return y1 + y2;
}
@@ -672,7 +672,7 @@ void test17() {
range(i); // $ range===50
i = 20 + (j -= 10);
- range(i); // $ range="==... += ...:... = ...+10" range===60
+ range(i); // $ range="==Store: ... += ...:Store: ... = ...+10" range===60
}
// Tests for unsigned multiplication.
@@ -693,7 +693,7 @@ int test_unsigned_mult01(unsigned int a, unsigned b) {
int r = a*b; // 0 .. 253
range(r);// $ range=>=0 range=<=253
total += r;
- range(total); // $ range=">=(unsigned int)...+0" range=>=0 range=<=506 range="<=(unsigned int)...+253"
+ range(total); // $ range=">=Phi: (unsigned int)...+0" range=>=0 range=<=506 range="<=Phi: (unsigned int)...+253"
}
if (3 <= a && a <= 11 && 13 <= b && b <= 23) {
range(a); // $ range=<=11 range=>=3
@@ -701,9 +701,9 @@ int test_unsigned_mult01(unsigned int a, unsigned b) {
int r = a*b; // 39 .. 253
range(r); // $ range=>=39 range=<=253
total += r;
- range(total); // $ range=>=39 range=<=759 range="<=(unsigned int)...+253" range="<=(unsigned int)...+506" range=">=(unsigned int)...+39"
+ range(total); // $ range=>=39 range=<=759 range="<=Phi: (unsigned int)...+253" range="<=Phi: (unsigned int)...+506" range=">=Phi: (unsigned int)...+39"
}
- range(total); // $ range=>=0 range=<=759 range=">=(unsigned int)...+0" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
+ range(total); // $ range=>=0 range=<=759 range=">=Phi: (unsigned int)...+0" range="<=Phi: (unsigned int)...+506" range="<=Phi: (unsigned int)...+253"
return total;
}
@@ -722,16 +722,16 @@ int test_unsigned_mult02(unsigned b) {
int r = 11*b; // 0 .. 253
range(r); // $ range=>=0 range=<=253
total += r;
- range(total); // $ range=">=(unsigned int)...+0" range=>=0 range="<=(unsigned int)...+253" range=<=506
+ range(total); // $ range=">=Phi: (unsigned int)...+0" range=>=0 range="<=Phi: (unsigned int)...+253" range=<=506
}
if (13 <= b && b <= 23) {
range(b); // $ range=<=23 range=>=13
int r = 11*b; // 143 .. 253
range(r); // $ range=>=143 range=<=253
total += r;
- range(total); // $ range="<=(unsigned int)...+253" range="<=(unsigned int)...+506" range=">=(unsigned int)...+143" range=>=143 range=<=759
+ range(total); // $ range="<=Phi: (unsigned int)...+253" range="<=Phi: (unsigned int)...+506" range=">=Phi: (unsigned int)...+143" range=>=143 range=<=759
}
- range(total); // $ range=>=0 range=<=759 range=">=(unsigned int)...+0" range="<=(unsigned int)...+506" range="<=(unsigned int)...+253"
+ range(total); // $ range=>=0 range=<=759 range=">=Phi: (unsigned int)...+0" range="<=Phi: (unsigned int)...+506" range="<=Phi: (unsigned int)...+253"
return total;
}
@@ -851,7 +851,7 @@ int notequal_type_endpoint(unsigned n) {
n--; // 1 ..
}
- range(n); // $ range=<=n+0 // 0 .. 0
+ range(n); // $ range="<=InitializeParameter: n+0" // 0 .. 0
}
void notequal_refinement(short n) {
@@ -946,7 +946,7 @@ void widen_recursive_expr() {
for (s = 0; s < 10; s++) {
range(s); // $ range=<=9 range=>=0
int result = s + s;
- range(result); // $ range=<=18 range=<=s+9 range=>=0 range=>=s+0
+ range(result); // $ range=<=18 Unexpected result: range="<=Phi: s+9" range=>=0 Unexpected result: range=">=Phi: s+0"
}
}
@@ -974,7 +974,7 @@ void test_mod_neg(int s) {
void test_mod_ternary(int s, bool b) {
int s2 = s % (b ? 5 : 500);
- range(s2); // $ range=>=-499 range=<=499 range="<=... ? ... : ...-1"
+ range(s2); // $ range=>=-499 range=<=499 range="<=Phi: ... ? ... : ...-1"
}
void test_mod_ternary2(int s, bool b1, bool b2) {
diff --git a/cpp/ql/test/library-tests/ir/range-analysis/test.cpp b/cpp/ql/test/library-tests/ir/range-analysis/test.cpp
index 10b4c1a9a22..5d816f3cda4 100644
--- a/cpp/ql/test/library-tests/ir/range-analysis/test.cpp
+++ b/cpp/ql/test/library-tests/ir/range-analysis/test.cpp
@@ -16,8 +16,8 @@
int sum = x + y; // $ overflow=+-
} else {
if (y > 300) {
- range(x); // $ range=>=302 range=<=400 range=<=y+1 MISSING: range===y+1
- range(y); // $ range=>=301 range=<=399 range===x-1
+ range(x); // $ range=>=302 range=<=400 range="<=InitializeParameter: y+1" MISSING: range===y+1
+ range(y); // $ range=>=301 range=<=399 range="==InitializeParameter: x:Store: x-1"
int sum = x + y;
}
}
@@ -39,9 +39,9 @@
}
if (y == x - 1 && y > 300 && y + 2 == z && z == 350) { // $ overflow=+ overflow=-
- range(x); // $ range===349 range===y+1 range===z-1
- range(y); // $ range===348 range=>=x-1 range===z-2 MISSING: range===x-1
- range(z); // $ range===350 range=<=y+2 MISSING: range===x+1 range===y+2
+ range(x); // $ range===349 range="==InitializeParameter: y+1" range="==InitializeParameter: z-1"
+ range(y); // $ range===348 range=">=InitializeParameter: x:Store: x-1" range="==InitializeParameter: z-2" MISSING: range===x-1
+ range(z); // $ range===350 range="<=InitializeParameter: y+2" MISSING: range===x+1 range===y+2
return x + y + z;
}
}
@@ -56,7 +56,7 @@
while (f3_get(n)) n+=2;
for (int i = 0; i < n; i += 2) {
- range(i); // $ range=>=0 SPURIOUS: range="<=call to f3_get-1" range="<=call to f3_get-2"
+ range(i); // $ range=>=0 SPURIOUS: range="<=Phi: call to f3_get-1" range="<=Phi: call to f3_get-2"
}
}
From 74a585222cb287157232a743bcd01f47f1933fe8 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Thu, 25 May 2023 15:52:56 +0200
Subject: [PATCH 120/813] C#: Extract source files generated by source
generators
---
.../Semmle.Extraction.CSharp/Extractor/Extractor.cs | 11 ++++++++++-
csharp/tools/tracing-config.lua | 9 +++++----
2 files changed, 15 insertions(+), 5 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
index ec4f44c21c7..79855875d02 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Extractor/Extractor.cs
@@ -381,8 +381,17 @@ namespace Semmle.Extraction.CSharp
references => ResolveReferences(compilerArguments, analyser, canonicalPathCache, references),
(analyser, syntaxTrees) =>
{
+ var paths = compilerArguments.SourceFiles
+ .Select(src => src.Path)
+ .ToList();
+
+ if (compilerArguments.GeneratedFilesOutputDirectory is not null)
+ {
+ paths.AddRange(Directory.GetFiles(compilerArguments.GeneratedFilesOutputDirectory, "*.cs", SearchOption.AllDirectories));
+ }
+
return ReadSyntaxTrees(
- compilerArguments.SourceFiles.Select(src => canonicalPathCache.GetCanonicalPath(src.Path)),
+ paths.Select(canonicalPathCache.GetCanonicalPath),
analyser,
compilerArguments.ParseOptions,
compilerArguments.Encoding,
diff --git a/csharp/tools/tracing-config.lua b/csharp/tools/tracing-config.lua
index 2db04d83524..79b2ea2ca1c 100644
--- a/csharp/tools/tracing-config.lua
+++ b/csharp/tools/tracing-config.lua
@@ -63,7 +63,7 @@ function RegisterExtractorPack(id)
end
end
if match then
- local injections = { '-p:UseSharedCompilation=false' }
+ local injections = { '-p:UseSharedCompilation=false', '-p:EmitCompilerGeneratedFiles=true' }
if dotnetRunNeedsSeparator then
table.insert(injections, '--')
end
@@ -118,7 +118,8 @@ function RegisterExtractorPack(id)
compilerArguments,
nil, {
'/p:UseSharedCompilation=false',
- '/p:MvcBuildViews=true'
+ '/p:MvcBuildViews=true',
+ '/p:EmitCompilerGeneratedFiles=true',
})
}
@@ -154,7 +155,7 @@ function RegisterExtractorPack(id)
if seenCompilerCall then
return {
- order = ORDER_BEFORE,
+ order = ORDER_AFTER,
invocation = {
path = AbsolutifyExtractorPath(id, extractor),
arguments = {
@@ -194,7 +195,7 @@ function RegisterExtractorPack(id)
if seenCompilerCall then
return {
- order = ORDER_BEFORE,
+ order = ORDER_AFTER,
invocation = {
path = AbsolutifyExtractorPath(id, extractor),
arguments = {
From 736f2871f9cc61c078e1839d12f255926861613e Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Thu, 25 May 2023 22:02:32 +0100
Subject: [PATCH 121/813] Swift: Tweak private info regexps to restore
'account_no' results.
---
.../ql/lib/codeql/swift/security/SensitiveExprs.qll | 2 +-
.../Security/CWE-311/CleartextTransmission.expected | 4 ++++
.../Security/CWE-311/SensitiveExprs.expected | 1 +
.../test/query-tests/Security/CWE-311/testURL.swift | 2 +-
.../CWE-328/WeakSensitiveDataHashing.expected | 12 ++++++++++++
.../query-tests/Security/CWE-328/testCryptoKit.swift | 12 ++++++------
6 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
index b6f5c231823..71a250229e7 100644
--- a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
+++ b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
@@ -58,7 +58,7 @@ class SensitivePrivateInfo extends SensitiveDataType, TPrivateInfo {
// Geographic location - where the user is (or was)
"latitude|longitude|" +
// Financial data - such as credit card numbers, salary, bank accounts, and debts
- "credit.?card|debit.?card|salary|bank.?account|" +
+ "credit.?card|debit.?card|salary|bank.?account|acc(ou)?nt.?(no|num)|" +
// Communications - e-mail addresses, private e-mail messages, SMS text messages, chat logs, etc.
"email|" +
// Health - medical conditions, insurance status, prescription records
diff --git a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
index 94272faf6d0..c832cf51a15 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
+++ b/swift/ql/test/query-tests/Security/CWE-311/CleartextTransmission.expected
@@ -13,6 +13,7 @@ edges
| testSend.swift:54:17:54:17 | password | testSend.swift:41:10:41:18 | data |
| testSend.swift:54:17:54:17 | password | testSend.swift:54:13:54:25 | call to pad(_:) |
| testURL.swift:13:54:13:54 | passwd | testURL.swift:13:22:13:54 | ... .+(_:_:) ... |
+| testURL.swift:15:55:15:55 | account_no | testURL.swift:15:22:15:55 | ... .+(_:_:) ... |
| testURL.swift:16:55:16:55 | credit_card_no | testURL.swift:16:22:16:55 | ... .+(_:_:) ... |
nodes
| file://:0:0:0:0 | [summary] to write: return (return) in Data.init(_:) | semmle.label | [summary] to write: return (return) in Data.init(_:) |
@@ -40,6 +41,8 @@ nodes
| testSend.swift:66:27:66:30 | .mobileNumber | semmle.label | .mobileNumber |
| testURL.swift:13:22:13:54 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| testURL.swift:13:54:13:54 | passwd | semmle.label | passwd |
+| testURL.swift:15:22:15:55 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
+| testURL.swift:15:55:15:55 | account_no | semmle.label | account_no |
| testURL.swift:16:22:16:55 | ... .+(_:_:) ... | semmle.label | ... .+(_:_:) ... |
| testURL.swift:16:55:16:55 | credit_card_no | semmle.label | credit_card_no |
| testURL.swift:20:22:20:22 | passwd | semmle.label | passwd |
@@ -58,5 +61,6 @@ subpaths
| testSend.swift:65:27:65:27 | license_key | testSend.swift:65:27:65:27 | license_key | testSend.swift:65:27:65:27 | license_key | This operation transmits 'license_key', which may contain unencrypted sensitive data from $@. | testSend.swift:65:27:65:27 | license_key | license_key |
| testSend.swift:66:27:66:30 | .mobileNumber | testSend.swift:66:27:66:30 | .mobileNumber | testSend.swift:66:27:66:30 | .mobileNumber | This operation transmits '.mobileNumber', which may contain unencrypted sensitive data from $@. | testSend.swift:66:27:66:30 | .mobileNumber | .mobileNumber |
| testURL.swift:13:22:13:54 | ... .+(_:_:) ... | testURL.swift:13:54:13:54 | passwd | testURL.swift:13:22:13:54 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:13:54:13:54 | passwd | passwd |
+| testURL.swift:15:22:15:55 | ... .+(_:_:) ... | testURL.swift:15:55:15:55 | account_no | testURL.swift:15:22:15:55 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:15:55:15:55 | account_no | account_no |
| testURL.swift:16:22:16:55 | ... .+(_:_:) ... | testURL.swift:16:55:16:55 | credit_card_no | testURL.swift:16:22:16:55 | ... .+(_:_:) ... | This operation transmits '... .+(_:_:) ...', which may contain unencrypted sensitive data from $@. | testURL.swift:16:55:16:55 | credit_card_no | credit_card_no |
| testURL.swift:20:22:20:22 | passwd | testURL.swift:20:22:20:22 | passwd | testURL.swift:20:22:20:22 | passwd | This operation transmits 'passwd', which may contain unencrypted sensitive data from $@. | testURL.swift:20:22:20:22 | passwd | passwd |
diff --git a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
index 89b8d36ccdf..69de9137213 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
+++ b/swift/ql/test/query-tests/Security/CWE-311/SensitiveExprs.expected
@@ -129,5 +129,6 @@
| testSend.swift:66:27:66:30 | .mobileNumber | label:mobileNumber, type:private information |
| testSend.swift:69:27:69:30 | .passwordFeatureEnabled | label:passwordFeatureEnabled, type:credential |
| testURL.swift:13:54:13:54 | passwd | label:passwd, type:credential |
+| testURL.swift:15:55:15:55 | account_no | label:account_no, type:private information |
| testURL.swift:16:55:16:55 | credit_card_no | label:credit_card_no, type:private information |
| testURL.swift:20:22:20:22 | passwd | label:passwd, type:credential |
diff --git a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
index 48ae815232f..7f9b64ff4f6 100644
--- a/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
+++ b/swift/ql/test/query-tests/Security/CWE-311/testURL.swift
@@ -12,7 +12,7 @@ struct URL
func test1(passwd : String, encrypted_passwd : String, account_no : String, credit_card_no : String) {
let a = URL(string: "http://example.com/login?p=" + passwd); // BAD
let b = URL(string: "http://example.com/login?p=" + encrypted_passwd); // GOOD (not sensitive)
- let c = URL(string: "http://example.com/login?ac=" + account_no); // BAD [NOT DETECTED]
+ let c = URL(string: "http://example.com/login?ac=" + account_no); // BAD
let d = URL(string: "http://example.com/login?cc=" + credit_card_no); // BAD
let base = URL(string: "http://example.com/"); // GOOD (not sensitive)
diff --git a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
index b6c7161b853..a23916e1a5c 100644
--- a/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
+++ b/swift/ql/test/query-tests/Security/CWE-328/WeakSensitiveDataHashing.expected
@@ -2,22 +2,28 @@ edges
nodes
| testCryptoKit.swift:56:47:56:47 | passwd | semmle.label | passwd |
| testCryptoKit.swift:57:43:57:43 | cert | semmle.label | cert |
+| testCryptoKit.swift:59:43:59:43 | account_no | semmle.label | account_no |
| testCryptoKit.swift:60:43:60:43 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:61:43:61:43 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:63:44:63:44 | passwd | semmle.label | passwd |
| testCryptoKit.swift:64:44:64:44 | cert | semmle.label | cert |
+| testCryptoKit.swift:66:44:66:44 | account_no | semmle.label | account_no |
| testCryptoKit.swift:67:44:67:44 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:90:23:90:23 | passwd | semmle.label | passwd |
| testCryptoKit.swift:91:23:91:23 | cert | semmle.label | cert |
+| testCryptoKit.swift:93:23:93:23 | account_no | semmle.label | account_no |
| testCryptoKit.swift:94:23:94:23 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:99:23:99:23 | passwd | semmle.label | passwd |
| testCryptoKit.swift:100:23:100:23 | cert | semmle.label | cert |
+| testCryptoKit.swift:102:23:102:23 | account_no | semmle.label | account_no |
| testCryptoKit.swift:103:23:103:23 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:132:32:132:32 | passwd | semmle.label | passwd |
| testCryptoKit.swift:133:32:133:32 | cert | semmle.label | cert |
+| testCryptoKit.swift:135:32:135:32 | account_no | semmle.label | account_no |
| testCryptoKit.swift:136:32:136:32 | credit_card_no | semmle.label | credit_card_no |
| testCryptoKit.swift:141:32:141:32 | passwd | semmle.label | passwd |
| testCryptoKit.swift:142:32:142:32 | cert | semmle.label | cert |
+| testCryptoKit.swift:144:32:144:32 | account_no | semmle.label | account_no |
| testCryptoKit.swift:145:32:145:32 | credit_card_no | semmle.label | credit_card_no |
| testCryptoSwift.swift:113:30:113:30 | passwdArray | semmle.label | passwdArray |
| testCryptoSwift.swift:115:31:115:31 | passwdArray | semmle.label | passwdArray |
@@ -33,22 +39,28 @@ subpaths
#select
| testCryptoKit.swift:56:47:56:47 | passwd | testCryptoKit.swift:56:47:56:47 | passwd | testCryptoKit.swift:56:47:56:47 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:56:47:56:47 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:57:43:57:43 | cert | testCryptoKit.swift:57:43:57:43 | cert | testCryptoKit.swift:57:43:57:43 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:57:43:57:43 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:59:43:59:43 | account_no | testCryptoKit.swift:59:43:59:43 | account_no | testCryptoKit.swift:59:43:59:43 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:59:43:59:43 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:60:43:60:43 | credit_card_no | testCryptoKit.swift:60:43:60:43 | credit_card_no | testCryptoKit.swift:60:43:60:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:60:43:60:43 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:61:43:61:43 | credit_card_no | testCryptoKit.swift:61:43:61:43 | credit_card_no | testCryptoKit.swift:61:43:61:43 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:61:43:61:43 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:63:44:63:44 | passwd | testCryptoKit.swift:63:44:63:44 | passwd | testCryptoKit.swift:63:44:63:44 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:63:44:63:44 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:64:44:64:44 | cert | testCryptoKit.swift:64:44:64:44 | cert | testCryptoKit.swift:64:44:64:44 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:64:44:64:44 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:66:44:66:44 | account_no | testCryptoKit.swift:66:44:66:44 | account_no | testCryptoKit.swift:66:44:66:44 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:66:44:66:44 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:67:44:67:44 | credit_card_no | testCryptoKit.swift:67:44:67:44 | credit_card_no | testCryptoKit.swift:67:44:67:44 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:67:44:67:44 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:90:23:90:23 | passwd | testCryptoKit.swift:90:23:90:23 | passwd | testCryptoKit.swift:90:23:90:23 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:90:23:90:23 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:91:23:91:23 | cert | testCryptoKit.swift:91:23:91:23 | cert | testCryptoKit.swift:91:23:91:23 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:91:23:91:23 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:93:23:93:23 | account_no | testCryptoKit.swift:93:23:93:23 | account_no | testCryptoKit.swift:93:23:93:23 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:93:23:93:23 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:94:23:94:23 | credit_card_no | testCryptoKit.swift:94:23:94:23 | credit_card_no | testCryptoKit.swift:94:23:94:23 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:94:23:94:23 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:99:23:99:23 | passwd | testCryptoKit.swift:99:23:99:23 | passwd | testCryptoKit.swift:99:23:99:23 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:99:23:99:23 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:100:23:100:23 | cert | testCryptoKit.swift:100:23:100:23 | cert | testCryptoKit.swift:100:23:100:23 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:100:23:100:23 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:102:23:102:23 | account_no | testCryptoKit.swift:102:23:102:23 | account_no | testCryptoKit.swift:102:23:102:23 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:102:23:102:23 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:103:23:103:23 | credit_card_no | testCryptoKit.swift:103:23:103:23 | credit_card_no | testCryptoKit.swift:103:23:103:23 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:103:23:103:23 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:132:32:132:32 | passwd | testCryptoKit.swift:132:32:132:32 | passwd | testCryptoKit.swift:132:32:132:32 | passwd | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:132:32:132:32 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:133:32:133:32 | cert | testCryptoKit.swift:133:32:133:32 | cert | testCryptoKit.swift:133:32:133:32 | cert | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:133:32:133:32 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:135:32:135:32 | account_no | testCryptoKit.swift:135:32:135:32 | account_no | testCryptoKit.swift:135:32:135:32 | account_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:135:32:135:32 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:136:32:136:32 | credit_card_no | testCryptoKit.swift:136:32:136:32 | credit_card_no | testCryptoKit.swift:136:32:136:32 | credit_card_no | Insecure hashing algorithm (MD5) depends on $@. | testCryptoKit.swift:136:32:136:32 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoKit.swift:141:32:141:32 | passwd | testCryptoKit.swift:141:32:141:32 | passwd | testCryptoKit.swift:141:32:141:32 | passwd | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:141:32:141:32 | passwd | sensitive data (credential passwd) |
| testCryptoKit.swift:142:32:142:32 | cert | testCryptoKit.swift:142:32:142:32 | cert | testCryptoKit.swift:142:32:142:32 | cert | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:142:32:142:32 | cert | sensitive data (credential cert) |
+| testCryptoKit.swift:144:32:144:32 | account_no | testCryptoKit.swift:144:32:144:32 | account_no | testCryptoKit.swift:144:32:144:32 | account_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:144:32:144:32 | account_no | sensitive data (private information account_no) |
| testCryptoKit.swift:145:32:145:32 | credit_card_no | testCryptoKit.swift:145:32:145:32 | credit_card_no | testCryptoKit.swift:145:32:145:32 | credit_card_no | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoKit.swift:145:32:145:32 | credit_card_no | sensitive data (private information credit_card_no) |
| testCryptoSwift.swift:113:30:113:30 | passwdArray | testCryptoSwift.swift:113:30:113:30 | passwdArray | testCryptoSwift.swift:113:30:113:30 | passwdArray | Insecure hashing algorithm (MD5) depends on $@. | testCryptoSwift.swift:113:30:113:30 | passwdArray | sensitive data (credential passwdArray) |
| testCryptoSwift.swift:115:31:115:31 | passwdArray | testCryptoSwift.swift:115:31:115:31 | passwdArray | testCryptoSwift.swift:115:31:115:31 | passwdArray | Insecure hashing algorithm (SHA1) depends on $@. | testCryptoSwift.swift:115:31:115:31 | passwdArray | sensitive data (credential passwdArray) |
diff --git a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
index 4e6f301c84b..55fb9284fa6 100644
--- a/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
+++ b/swift/ql/test/query-tests/Security/CWE-328/testCryptoKit.swift
@@ -56,14 +56,14 @@ func testHashMethods(passwd : UnsafeRawBufferPointer, cert: String, encrypted_pa
var hash = Crypto.Insecure.MD5.hash(data: passwd) // BAD
hash = Crypto.Insecure.MD5.hash(data: cert) // BAD
hash = Crypto.Insecure.MD5.hash(data: encrypted_passwd) // GOOD (not sensitive)
- hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD [NOT DETECTED]
+ hash = Crypto.Insecure.MD5.hash(data: account_no) // BAD
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD
hash = Crypto.Insecure.MD5.hash(data: credit_card_no) // BAD
hash = Crypto.Insecure.SHA1.hash(data: passwd) // BAD
hash = Crypto.Insecure.SHA1.hash(data: cert) // BAD
hash = Crypto.Insecure.SHA1.hash(data: encrypted_passwd) // GOOD (not sensitive)
- hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD [NOT DETECTED]
+ hash = Crypto.Insecure.SHA1.hash(data: account_no) // BAD
hash = Crypto.Insecure.SHA1.hash(data: credit_card_no) // BAD
hash = Crypto.SHA256.hash(data: passwd) // BAD [NOT DETECTED] not a computationally expensive hash
@@ -90,7 +90,7 @@ func testMD5UpdateWithData(passwd : String, cert: String, encrypted_passwd : Str
hash.update(data: passwd) // BAD
hash.update(data: cert) // BAD
hash.update(data: encrypted_passwd) // GOOD (not sensitive)
- hash.update(data: account_no) // BAD [NOT DETECTED]
+ hash.update(data: account_no) // BAD
hash.update(data: credit_card_no) // BAD
}
@@ -99,7 +99,7 @@ func testSHA1UpdateWithData(passwd : String, cert: String, encrypted_passwd : St
hash.update(data: passwd) // BAD
hash.update(data: cert) // BAD
hash.update(data: encrypted_passwd) // GOOD (not sensitive)
- hash.update(data: account_no) // BAD [NOT DETECTED]
+ hash.update(data: account_no) // BAD
hash.update(data: credit_card_no) // BAD
}
@@ -132,7 +132,7 @@ func testMD5UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, ce
hash.update(bufferPointer: passwd) // BAD
hash.update(bufferPointer: cert) // BAD
hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive)
- hash.update(bufferPointer: account_no) // BAD [NOT DETECTED]
+ hash.update(bufferPointer: account_no) // BAD
hash.update(bufferPointer: credit_card_no) // BAD
}
@@ -141,7 +141,7 @@ func testSHA1UpdateWithUnsafeRawBufferPointer(passwd : UnsafeRawBufferPointer, c
hash.update(bufferPointer: passwd) // BAD
hash.update(bufferPointer: cert) // BAD
hash.update(bufferPointer: encrypted_passwd) // GOOD (not sensitive)
- hash.update(bufferPointer: account_no) // BAD [NOT DETECTED]
+ hash.update(bufferPointer: account_no) // BAD
hash.update(bufferPointer: credit_card_no) // BAD
}
From 918cfd6f44ca7f2b7931fbf6e6ebaff2096c41ab Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Fri, 26 May 2023 09:50:06 +0200
Subject: [PATCH 122/813] Add integration test
---
.../all-platforms/cshtml/Files.expected | 6 ++++++
.../all-platforms/cshtml/Files.ql | 5 +++++
.../all-platforms/cshtml/Program.cs | 1 +
.../all-platforms/cshtml/Views/Home/Index.cshtml | 8 ++++++++
.../all-platforms/cshtml/cshtml.csproj | 14 ++++++++++++++
.../integration-tests/all-platforms/cshtml/test.py | 3 +++
6 files changed, 37 insertions(+)
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/Files.expected
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/Files.ql
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/Program.cs
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/Views/Home/Index.cshtml
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/cshtml.csproj
create mode 100644 csharp/ql/integration-tests/all-platforms/cshtml/test.py
diff --git a/csharp/ql/integration-tests/all-platforms/cshtml/Files.expected b/csharp/ql/integration-tests/all-platforms/cshtml/Files.expected
new file mode 100644
index 00000000000..86a8cd34b88
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/cshtml/Files.expected
@@ -0,0 +1,6 @@
+| Program.cs:0:0:0:0 | Program.cs |
+| obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs:0:0:0:0 | obj/Debug/net7.0/.NETCoreApp,Version=v7.0.AssemblyAttributes.cs |
+| obj/Debug/net7.0/cshtml.AssemblyInfo.cs:0:0:0:0 | obj/Debug/net7.0/cshtml.AssemblyInfo.cs |
+| obj/Debug/net7.0/cshtml.GlobalUsings.g.cs:0:0:0:0 | obj/Debug/net7.0/cshtml.GlobalUsings.g.cs |
+| obj/Debug/net7.0/cshtml.RazorAssemblyInfo.cs:0:0:0:0 | obj/Debug/net7.0/cshtml.RazorAssemblyInfo.cs |
+| obj/Debug/net7.0/generated/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Views_Home_Index_cshtml.g.cs:0:0:0:0 | obj/Debug/net7.0/generated/Microsoft.NET.Sdk.Razor.SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator/Views_Home_Index_cshtml.g.cs |
diff --git a/csharp/ql/integration-tests/all-platforms/cshtml/Files.ql b/csharp/ql/integration-tests/all-platforms/cshtml/Files.ql
new file mode 100644
index 00000000000..bea5557a25f
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/cshtml/Files.ql
@@ -0,0 +1,5 @@
+import csharp
+
+from File f
+where f.fromSource()
+select f
diff --git a/csharp/ql/integration-tests/all-platforms/cshtml/Program.cs b/csharp/ql/integration-tests/all-platforms/cshtml/Program.cs
new file mode 100644
index 00000000000..47eee48cc79
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/cshtml/Program.cs
@@ -0,0 +1 @@
+var dummy = "dummy";
\ No newline at end of file
diff --git a/csharp/ql/integration-tests/all-platforms/cshtml/Views/Home/Index.cshtml b/csharp/ql/integration-tests/all-platforms/cshtml/Views/Home/Index.cshtml
new file mode 100644
index 00000000000..52ffe012e42
--- /dev/null
+++ b/csharp/ql/integration-tests/all-platforms/cshtml/Views/Home/Index.cshtml
@@ -0,0 +1,8 @@
+@{
+ ViewData["Title"] = "Home Page";
+}
+
+
+
+
\ No newline at end of file
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.ql b/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.ql
new file mode 100644
index 00000000000..02d554cffb6
--- /dev/null
+++ b/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.ql
@@ -0,0 +1,68 @@
+/**
+ * @name Unsafe Deserialization of user-controlled data by Plist
+ * @description Deserializing user-controlled data may allow attackers to
+ * execute arbitrary code.
+ * @kind path-problem
+ * @problem.severity warning
+ * @security-severity 9.8
+ * @precision high
+ * @id rb/Plist-unsafe-deserialization
+ * @tags security
+ * experimental
+ * external/cwe/cwe-502
+ */
+
+import codeql.ruby.ApiGraphs
+import codeql.ruby.DataFlow
+import codeql.ruby.TaintTracking
+import codeql.ruby.CFG
+import DataFlow::PathGraph
+import codeql.ruby.security.UnsafeDeserializationCustomizations
+
+abstract class PlistUnsafeSinks extends DataFlow::Node { }
+
+/**
+ * check whether an input argument has desired "key: value" input or not.
+ * borrowed from UnsafeDeserialization module with some changes
+ */
+predicate checkkeyBalue(CfgNodes::ExprNodes::PairCfgNode p, string key, string value) {
+ p.getKey().getConstantValue().isStringlikeValue(key) and
+ DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().toString() = value
+}
+
+/**
+ * An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
+ * the default), considered a sink for unsafe deserialization.
+ * borrowed from UnsafeDeserialization module with some changes
+ */
+class UnsafePlistParsexmlArgument extends PlistUnsafeSinks {
+ UnsafePlistParsexmlArgument() {
+ exists(DataFlow::CallNode plistParsexml |
+ plistParsexml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
+ |
+ this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
+ // Exclude calls that explicitly pass a safe mode option.
+ checkkeyBalue(plistParsexml.getArgument(1).asExpr(), "marshal", "true")
+ or
+ this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
+ plistParsexml.getNumberOfArguments() = 1
+ )
+ }
+}
+
+class Configuration extends TaintTracking::Configuration {
+ Configuration() { this = "PlistUnsafeDeserialization" }
+
+ override predicate isSource(DataFlow::Node source) {
+ // to detect CVE-2021-33575, we should uncomment following line instead of current UnsafeDeserialization::Source
+ // source instanceof DataFlow::LocalSourceNode
+ source instanceof UnsafeDeserialization::Source
+ }
+
+ override predicate isSink(DataFlow::Node sink) { sink instanceof PlistUnsafeSinks }
+}
+
+from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
+where config.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
+ "potentially untrusted source"
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.rb b/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.rb
new file mode 100644
index 00000000000..63f64b5cd22
--- /dev/null
+++ b/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.rb
@@ -0,0 +1,13 @@
+require 'yaml'
+class UsersController < ActionController::Base
+ def example
+ # not safe
+ result = Plist.parse_xml(params[:yaml_string])
+ result = Plist.parse_xml(params[:yaml_string], marshal: true)
+
+ # safe
+ result = Plist.parse_xml(params[:yaml_string], marshal: false)
+ end
+end
+
+
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp
index 821747e0b52..c3a74527483 100644
--- a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp
+++ b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp
@@ -17,4 +17,10 @@
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
+
\ No newline at end of file
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
index a3f42a9c84e..5cb720d19da 100644
--- a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
+++ b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
@@ -1,5 +1,5 @@
/**
- * @name Deserialization of user-controlled data by YAML
+ * @name Unsafe Deserialization of user-controlled data by YAML
* @description Deserializing user-controlled data may allow attackers to
* execute arbitrary code.
* @kind path-problem
@@ -18,10 +18,10 @@ import codeql.ruby.TaintTracking
import DataFlow::PathGraph
import codeql.ruby.security.UnsafeDeserializationCustomizations
-abstract class YamlSink extends DataFlow::Node { }
+abstract class YamlUnsafeSinks extends DataFlow::Node { }
-class YamlUnsafeLoadArgument extends YamlSink {
- YamlUnsafeLoadArgument() {
+class YamlUnsafeArgument extends YamlUnsafeSinks {
+ YamlUnsafeArgument() {
this =
API::getTopLevelMember(["YAML", "Psych"])
.getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"])
@@ -45,7 +45,7 @@ class YamlUnsafeLoadArgument extends YamlSink {
}
class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "UnsafeYAMLDeserialization" }
+ Configuration() { this = "YamlUnsafeDeserialization" }
override predicate isSource(DataFlow::Node source) {
// to detect CVE-2022-32224, we should uncomment following line instead of current UnsafeDeserialization::Source
@@ -57,7 +57,7 @@ class Configuration extends TaintTracking::Configuration {
// after changing the isSource for detecting CVE-2022-32224
// uncomment the following line only see the CVE sink not other files similar sinks
// sink.getLocation().getFile().toString().matches("%yaml_column%") and
- sink instanceof YamlSink
+ sink instanceof YamlUnsafeSinks
}
override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
@@ -84,5 +84,5 @@ class Configuration extends TaintTracking::Configuration {
from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "This file extraction depends on a $@.", source.getNode(),
+select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
"potentially untrusted source"
diff --git a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.expected
deleted file mode 100644
index c01afdfbe69..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.expected
+++ /dev/null
@@ -1,34 +0,0 @@
-edges
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby |
-nodes
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
-subpaths
-#select
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.qlref
deleted file mode 100644
index 7e9e87117f9..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/CWE-502/YAMLUnsafeYamlDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.rb b/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.rb
deleted file mode 100644
index 6e836a0a049..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/CWE-502/YAMLUnsafeYamlDeserialization.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'yaml'
-class UsersController < ActionController::Base
- def example
- # safe
- Psych.load(params[:yaml_string])
- Psych.load_file(params[:yaml_file])
- Psych.parse_stream(params[:yaml_string])
- Psych.parse(params[:yaml_string])
- Psych.parse_file(params[:yaml_file])
- # unsafe
- Psych.unsafe_load(params[:yaml_string])
- Psych.unsafe_load_file(params[:yaml_file])
- Psych.load_stream(params[:yaml_string])
- parse_output = Psych.parse_stream(params[:yaml_string])
- parse_output.to_ruby
- Psych.parse(params[:yaml_string]).to_ruby
- Psych.parse_file(params[:yaml_file]).to_ruby
-
- end
-end
-
-
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected
new file mode 100644
index 00000000000..f5da2a260c4
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected
@@ -0,0 +1,12 @@
+edges
+| PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] |
+| PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] |
+nodes
+| PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | semmle.label | call to params : |
+| PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | semmle.label | ...[...] |
+| PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | semmle.label | call to params : |
+| PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | semmle.label | ...[...] |
+subpaths
+#select
+| PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params | potentially untrusted source |
+| PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref
new file mode 100644
index 00000000000..74d9d37315a
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref
@@ -0,0 +1 @@
+experimental/cwe-502/PlistUnsafeYamlDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.rb b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.rb
new file mode 100644
index 00000000000..63f64b5cd22
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.rb
@@ -0,0 +1,13 @@
+require 'yaml'
+class UsersController < ActionController::Base
+ def example
+ # not safe
+ result = Plist.parse_xml(params[:yaml_string])
+ result = Plist.parse_xml(params[:yaml_string], marshal: true)
+
+ # safe
+ result = Plist.parse_xml(params[:yaml_string], marshal: false)
+ end
+end
+
+
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
index c01afdfbe69..489602044a7 100644
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
@@ -26,9 +26,9 @@ nodes
| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
subpaths
#select
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | This file extraction depends on a $@. | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
+| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
index 7e9e87117f9..87fe2520217 100644
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
@@ -1 +1 @@
-experimental/CWE-502/YAMLUnsafeYamlDeserialization.ql
\ No newline at end of file
+experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
\ No newline at end of file
From b9296d3df88084252074f69bae97e986095b3b83 Mon Sep 17 00:00:00 2001
From: amammad
Date: Sat, 25 Feb 2023 23:08:31 +0100
Subject: [PATCH 159/813] v2.1 fix file names
---
...qhelp => PlistUnsafeDeserialization.qhelp} | 0
...ation.ql => PlistUnsafeDeserialization.ql} | 0
...ation.rb => PlistUnsafeDeserialization.rb} | 0
....qhelp => YAMLUnsafeDeserialization.qhelp} | 0
...zation.ql => YAMLUnsafeDeserialization.ql} | 0
...zation.rb => YAMLUnsafeDeserialization.rb} | 0
.../PlistUnsafeDeserialization.expected | 12 +++++++
.../cwe-502/PlistUnsafeDeserialization.qlref | 1 +
...ation.rb => PlistUnsafeDeserialization.rb} | 0
.../PlistUnsafeYamlDeserialization.expected | 12 -------
.../PlistUnsafeYamlDeserialization.qlref | 1 -
.../YAMLUnsafeDeserialization.expected | 34 +++++++++++++++++++
.../cwe-502/YAMLUnsafeDeserialization.qlref | 1 +
...zation.rb => YAMLUnsafeDeserialization.rb} | 0
.../YAMLUnsafeYamlDeserialization.expected | 34 -------------------
.../YAMLUnsafeYamlDeserialization.qlref | 1 -
16 files changed, 48 insertions(+), 48 deletions(-)
rename ruby/ql/src/experimental/cwe-502/{PlistUnsafeYamlDeserialization.qhelp => PlistUnsafeDeserialization.qhelp} (100%)
rename ruby/ql/src/experimental/cwe-502/{PlistUnsafeYamlDeserialization.ql => PlistUnsafeDeserialization.ql} (100%)
rename ruby/ql/src/experimental/cwe-502/{PlistUnsafeYamlDeserialization.rb => PlistUnsafeDeserialization.rb} (100%)
rename ruby/ql/src/experimental/cwe-502/{YAMLUnsafeYamlDeserialization.qhelp => YAMLUnsafeDeserialization.qhelp} (100%)
rename ruby/ql/src/experimental/cwe-502/{YAMLUnsafeYamlDeserialization.ql => YAMLUnsafeDeserialization.ql} (100%)
rename ruby/ql/src/experimental/cwe-502/{YAMLUnsafeYamlDeserialization.rb => YAMLUnsafeDeserialization.rb} (100%)
create mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
create mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
rename ruby/ql/test/query-tests/experimental/Security/cwe-502/{PlistUnsafeYamlDeserialization.rb => PlistUnsafeDeserialization.rb} (100%)
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref
create mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
create mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
rename ruby/ql/test/query-tests/experimental/Security/cwe-502/{YAMLUnsafeYamlDeserialization.rb => YAMLUnsafeDeserialization.rb} (100%)
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.qhelp
rename to ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.ql b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.ql
rename to ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.rb b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/PlistUnsafeYamlDeserialization.rb
rename to ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.qhelp
rename to ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
rename to ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.rb b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/YAMLUnsafeYamlDeserialization.rb
rename to ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.rb
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
new file mode 100644
index 00000000000..967ef978a3b
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
@@ -0,0 +1,12 @@
+edges
+| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] |
+| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] |
+nodes
+| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | semmle.label | call to params : |
+| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | semmle.label | ...[...] |
+| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | semmle.label | call to params : |
+| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | semmle.label | ...[...] |
+subpaths
+#select
+| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | potentially untrusted source |
+| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
new file mode 100644
index 00000000000..f7bfbada7b5
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
@@ -0,0 +1 @@
+experimental/cwe-502/PlistUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.rb b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.rb
rename to ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.rb
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected
deleted file mode 100644
index f5da2a260c4..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.expected
+++ /dev/null
@@ -1,12 +0,0 @@
-edges
-| PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] |
-| PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] |
-nodes
-| PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | semmle.label | call to params : |
-| PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | semmle.label | ...[...] |
-| PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | semmle.label | call to params : |
-| PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | semmle.label | ...[...] |
-subpaths
-#select
-| PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeYamlDeserialization.rb:5:30:5:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeYamlDeserialization.rb:5:30:5:35 | call to params | potentially untrusted source |
-| PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeYamlDeserialization.rb:6:30:6:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeYamlDeserialization.rb:6:30:6:35 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref
deleted file mode 100644
index 74d9d37315a..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeYamlDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/cwe-502/PlistUnsafeYamlDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
new file mode 100644
index 00000000000..9a9bd05e514
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
@@ -0,0 +1,34 @@
+edges
+| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] |
+| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] |
+| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] |
+| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : |
+| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : |
+| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : |
+| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby |
+nodes
+| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : | semmle.label | ...[...] : |
+| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : | semmle.label | ...[...] : |
+| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | semmle.label | call to params : |
+| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
+subpaths
+#select
+| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
+| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
+| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
+| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
+| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
+| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
new file mode 100644
index 00000000000..e06e0921159
--- /dev/null
+++ b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
@@ -0,0 +1 @@
+experimental/cwe-502/YAMLUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.rb b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.rb
rename to ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
deleted file mode 100644
index 489602044a7..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.expected
+++ /dev/null
@@ -1,34 +0,0 @@
-edges
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby |
-nodes
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:14:39:14:58 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:16:17:16:36 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeYamlDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
-subpaths
-#select
-| YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:11:23:11:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeYamlDeserialization.rb:12:28:12:45 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeYamlDeserialization.rb:13:23:13:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeYamlDeserialization.rb:15:5:15:24 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeYamlDeserialization.rb:16:5:16:45 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
-| YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeYamlDeserialization.rb:17:5:17:48 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeYamlDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
deleted file mode 100644
index 87fe2520217..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeYamlDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/cwe-502/YAMLUnsafeYamlDeserialization.ql
\ No newline at end of file
From ad7e107ff57da219b2abc185fc8e80020b302082 Mon Sep 17 00:00:00 2001
From: amammad
Date: Wed, 1 Mar 2023 08:55:17 +0100
Subject: [PATCH 160/813] add the new YAML/PLIST sinks into the existing
rb/unsafe-deserialization query
---
.../UnsafeDeserializationCustomizations.qll | 59 ++++++++++++++++++-
.../security/UnsafeDeserializationQuery.qll | 22 +++++++
2 files changed, 79 insertions(+), 2 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index 45e0888eacd..96f7e87d7a2 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -75,13 +75,41 @@ module UnsafeDeserialization {
}
/**
- * An argument in a call to `YAML.load`, considered a sink
+ * An argument in a call to `YAML.unsafe_*` and `YAML.load_stream` , considered sinks
* for unsafe deserialization. The `YAML` module is an alias of `Psych` in
* recent versions of Ruby.
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
- this = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("load").getArgument(0)
+ this =
+ API::getTopLevelMember(["YAML", "Psych"])
+ .getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"])
+ .getArgument(0)
+ or
+ this =
+ API::getTopLevelMember(["YAML", "Psych"])
+ .getAMethodCall(["unsafe_load", "load_stream"])
+ .getKeywordArgument("yaml")
+ or
+ this =
+ API::getTopLevelMember(["YAML", "Psych"])
+ .getAMethodCall("unsafe_load_file")
+ .getKeywordArgument("filename")
+ }
+ }
+
+ /**
+ * An argument in a call to `YAML.parse*`, considered sinks
+ * for unsafe deserialization if there is a call to `to_ruby` on returned value of them,
+ * so this need some additional taint steps. The `YAML` module is an alias of `Psych` in
+ * recent versions of Ruby.
+ */
+ class YamlParseArgument extends Sink {
+ YamlParseArgument() {
+ this =
+ API::getTopLevelMember(["YAML", "Psych"])
+ .getAMethodCall(["parse", "parse_stream", "parse_file"])
+ .getAMethodCall("to_ruby")
}
}
@@ -208,4 +236,31 @@ module UnsafeDeserialization {
)
}
}
+
+ /**
+ * check whether an input argument has desired "key: value" input or not.
+ */
+ predicate checkkeyValue(CfgNodes::ExprNodes::PairCfgNode p, string key, string value) {
+ p.getKey().getConstantValue().isStringlikeValue(key) and
+ DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().toString() = value
+ }
+
+ /**
+ * An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
+ * the default), considered a sink for unsafe deserialization.
+ */
+ class UnsafePlistParsexmlArgument extends Sink {
+ UnsafePlistParsexmlArgument() {
+ exists(DataFlow::CallNode plistParsexml |
+ plistParsexml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
+ |
+ this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
+ // Exclude calls that explicitly pass a safe mode option.
+ checkkeyValue(plistParsexml.getArgument(1).asExpr(), "marshal", "true")
+ or
+ this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
+ plistParsexml.getNumberOfArguments() = 1
+ )
+ }
+ }
}
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
index dc6293dc10e..fed9160ccd9 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
@@ -9,6 +9,7 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.TaintTracking
+private import codeql.ruby.ApiGraphs
import UnsafeDeserializationCustomizations
/**
@@ -23,6 +24,27 @@ class Configuration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserialization::Sink }
+ override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ exists(DataFlow::CallNode yaml_parser_methods |
+ yaml_parser_methods =
+ API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
+ (
+ nodeFrom = yaml_parser_methods.getArgument(0) or
+ nodeFrom = yaml_parser_methods.getKeywordArgument("yaml")
+ ) and
+ nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
+ )
+ or
+ exists(DataFlow::CallNode yaml_parser_methods |
+ yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
+ (
+ nodeFrom = yaml_parser_methods.getArgument(0) or
+ nodeFrom = yaml_parser_methods.getKeywordArgument("filename")
+ ) and
+ nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
+ )
+ }
+
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof UnsafeDeserialization::Sanitizer
From e76ed9454ab924a1b9a1f2a99711180938ade110 Mon Sep 17 00:00:00 2001
From: amammad
Date: Wed, 1 Mar 2023 10:45:45 +0100
Subject: [PATCH 161/813] v3 add global taint steps for to_ruby of YAML/Psych
---
ruby/ql/lib/codeql/ruby/Frameworks.qll | 1 +
ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll | 30 +++++++++++++++++++
.../security/UnsafeDeserializationQuery.qll | 21 -------------
3 files changed, 31 insertions(+), 21 deletions(-)
create mode 100644 ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
diff --git a/ruby/ql/lib/codeql/ruby/Frameworks.qll b/ruby/ql/lib/codeql/ruby/Frameworks.qll
index 29eacf22e33..e6f2d9681f3 100644
--- a/ruby/ql/lib/codeql/ruby/Frameworks.qll
+++ b/ruby/ql/lib/codeql/ruby/Frameworks.qll
@@ -33,3 +33,4 @@ private import codeql.ruby.frameworks.Sinatra
private import codeql.ruby.frameworks.Twirp
private import codeql.ruby.frameworks.Sqlite3
private import codeql.ruby.frameworks.Pg
+private import codeql.ruby.frameworks.Yaml
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
new file mode 100644
index 00000000000..d1fe6547e56
--- /dev/null
+++ b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
@@ -0,0 +1,30 @@
+/**
+ * add additional steps for to_ruby method of YAML/Psych library
+ */
+
+private import codeql.ruby.dataflow.FlowSteps
+private import codeql.ruby.DataFlow
+private import codeql.ruby.ApiGraphs
+
+private class YamlParseStep extends AdditionalTaintStep {
+ override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
+ exists(DataFlow::CallNode yaml_parser_methods |
+ yaml_parser_methods =
+ API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
+ (
+ pred = yaml_parser_methods.getArgument(0) or
+ pred = yaml_parser_methods.getKeywordArgument("yaml")
+ ) and
+ succ = yaml_parser_methods.getAMethodCall("to_ruby")
+ )
+ or
+ exists(DataFlow::CallNode yaml_parser_methods |
+ yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
+ (
+ pred = yaml_parser_methods.getArgument(0) or
+ pred = yaml_parser_methods.getKeywordArgument("filename")
+ ) and
+ succ = yaml_parser_methods.getAMethodCall("to_ruby")
+ )
+ }
+}
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
index fed9160ccd9..c4c5a39e451 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
@@ -24,27 +24,6 @@ class Configuration extends TaintTracking::Configuration {
override predicate isSink(DataFlow::Node sink) { sink instanceof UnsafeDeserialization::Sink }
- override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods =
- API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
- (
- nodeFrom = yaml_parser_methods.getArgument(0) or
- nodeFrom = yaml_parser_methods.getKeywordArgument("yaml")
- ) and
- nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
- )
- or
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
- (
- nodeFrom = yaml_parser_methods.getArgument(0) or
- nodeFrom = yaml_parser_methods.getKeywordArgument("filename")
- ) and
- nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
- )
- }
-
override predicate isSanitizer(DataFlow::Node node) {
super.isSanitizer(node) or
node instanceof UnsafeDeserialization::Sanitizer
From 335441ce041893314eb377e154f4d11876f3eb6f Mon Sep 17 00:00:00 2001
From: amammad
Date: Mon, 24 Apr 2023 10:43:09 +0200
Subject: [PATCH 162/813] v4: make variable names camelCase, some inhancement,
remove some duplicates
---
ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll | 41 ++++++++++------
.../UnsafeDeserializationCustomizations.qll | 48 ++++++++-----------
.../security/UnsafeDeserializationQuery.qll | 1 -
.../cwe-502/PlistUnsafeDeserialization.qhelp | 7 ++-
.../cwe-502/PlistUnsafeDeserialization.ql | 25 +++-------
.../cwe-502/PlistUnsafeDeserialization.rb | 6 ++-
.../cwe-502/UnsafeDeserialization.qhelp | 23 +++++++++
.../examples/PlistUnsafeDeserialization.rb | 17 +++++++
.../examples/YAMLUnsafeDeserialization.rb | 22 +++++++++
9 files changed, 123 insertions(+), 67 deletions(-)
create mode 100644 ruby/ql/src/queries/security/cwe-502/examples/PlistUnsafeDeserialization.rb
create mode 100644 ruby/ql/src/queries/security/cwe-502/examples/YAMLUnsafeDeserialization.rb
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
index d1fe6547e56..81afdcbed7b 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
@@ -1,30 +1,43 @@
/**
- * add additional steps for to_ruby method of YAML/Psych library
+ * Provides modeling for the `YAML` and `Psych` libraries.
*/
private import codeql.ruby.dataflow.FlowSteps
private import codeql.ruby.DataFlow
private import codeql.ruby.ApiGraphs
+/**
+ * A taint step related to the result of `YAML.parse` calls, or similar.
+ *In the following example, this step will propagate taint from
+ *`source` to `sink`:
+ *
+ *```rb
+ *x = source
+ *result = YAML.parse(x)
+ *sink result.to_ruby # Unsafe call
+ * ```
+ */
private class YamlParseStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods =
- API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
+ exists(DataFlow::CallNode yamlParserMethod |
+ yamlParserMethod = yamlNode().getAMethodCall(["parse", "parse_stream"]) and
(
- pred = yaml_parser_methods.getArgument(0) or
- pred = yaml_parser_methods.getKeywordArgument("yaml")
+ pred = yamlParserMethod.getArgument(0) or
+ pred = yamlParserMethod.getKeywordArgument("yaml")
) and
- succ = yaml_parser_methods.getAMethodCall("to_ruby")
- )
- or
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
+ succ = yamlParserMethod.getAMethodCall("to_ruby")
+ or
+ yamlParserMethod = yamlNode().getAMethodCall("parse_file") and
(
- pred = yaml_parser_methods.getArgument(0) or
- pred = yaml_parser_methods.getKeywordArgument("filename")
+ pred = yamlParserMethod.getArgument(0) or
+ pred = yamlParserMethod.getKeywordArgument("filename")
) and
- succ = yaml_parser_methods.getAMethodCall("to_ruby")
+ succ = yamlParserMethod.getAMethodCall("to_ruby")
)
}
}
+
+/**
+ * YAML/Psych Top level Class member
+ */
+private API::Node yamlNode() { result = API::getTopLevelMember(["YAML", "Psych"]) }
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index 96f7e87d7a2..fcaceed1b3a 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -78,26 +78,27 @@ module UnsafeDeserialization {
* An argument in a call to `YAML.unsafe_*` and `YAML.load_stream` , considered sinks
* for unsafe deserialization. The `YAML` module is an alias of `Psych` in
* recent versions of Ruby.
+ * the `this = yamlNode().getAMethodCall("load").getArgument(0)` is safe
+ * in recent versions of YAML library, so it will be removed in future.
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"])
- .getArgument(0)
+ this = yamlNode().getAMethodCall("load").getArgument(0)
or
this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["unsafe_load", "load_stream"])
- .getKeywordArgument("yaml")
+ yamlNode().getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"]).getArgument(0)
or
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall("unsafe_load_file")
- .getKeywordArgument("filename")
+ this = yamlNode().getAMethodCall(["unsafe_load", "load_stream"]).getKeywordArgument("yaml")
+ or
+ this = yamlNode().getAMethodCall("unsafe_load_file").getKeywordArgument("filename")
}
}
+ /**
+ * YAML/Psych Top level Class member
+ */
+ private API::Node yamlNode() { result = API::getTopLevelMember(["YAML", "Psych"]) }
+
/**
* An argument in a call to `YAML.parse*`, considered sinks
* for unsafe deserialization if there is a call to `to_ruby` on returned value of them,
@@ -107,9 +108,7 @@ module UnsafeDeserialization {
class YamlParseArgument extends Sink {
YamlParseArgument() {
this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["parse", "parse_stream", "parse_file"])
- .getAMethodCall("to_ruby")
+ yamlNode().getAMethodCall(["parse", "parse_stream", "parse_file"]).getAMethodCall("to_ruby")
}
}
@@ -237,29 +236,20 @@ module UnsafeDeserialization {
}
}
- /**
- * check whether an input argument has desired "key: value" input or not.
- */
- predicate checkkeyValue(CfgNodes::ExprNodes::PairCfgNode p, string key, string value) {
- p.getKey().getConstantValue().isStringlikeValue(key) and
- DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().toString() = value
- }
-
/**
* An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
* the default), considered a sink for unsafe deserialization.
*/
class UnsafePlistParsexmlArgument extends Sink {
UnsafePlistParsexmlArgument() {
- exists(DataFlow::CallNode plistParsexml |
- plistParsexml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
+ exists(DataFlow::CallNode plistParseXml |
+ plistParseXml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
|
- this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
- // Exclude calls that explicitly pass a safe mode option.
- checkkeyValue(plistParsexml.getArgument(1).asExpr(), "marshal", "true")
+ this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
+ plistParseXml.getKeywordArgument("marshal").getConstantValue().isBoolean(true)
or
- this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
- plistParsexml.getNumberOfArguments() = 1
+ this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
+ plistParseXml.getNumberOfArguments() = 1
)
}
}
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
index c4c5a39e451..dc6293dc10e 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationQuery.qll
@@ -9,7 +9,6 @@
private import codeql.ruby.AST
private import codeql.ruby.DataFlow
private import codeql.ruby.TaintTracking
-private import codeql.ruby.ApiGraphs
import UnsafeDeserializationCustomizations
/**
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
index 80ec6c51dc0..45c4220c1b4 100644
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
+++ b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
@@ -8,18 +8,17 @@
- This vulnerability can be prevented by using Plist.parse_xml.
+ This vulnerability in Plist can be prevented by calling Plist.parse_xml FileOrXmlString, marshal: false.
-
In the example below, you can see safe and unsafe call of this dangerous method that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
+
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
\ No newline at end of file
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
index 02d554cffb6..4ba55824598 100644
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
+++ b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
@@ -6,7 +6,7 @@
* @problem.severity warning
* @security-severity 9.8
* @precision high
- * @id rb/Plist-unsafe-deserialization
+ * @id rb/plist-unsafe-deserialization
* @tags security
* experimental
* external/cwe/cwe-502
@@ -21,31 +21,20 @@ import codeql.ruby.security.UnsafeDeserializationCustomizations
abstract class PlistUnsafeSinks extends DataFlow::Node { }
-/**
- * check whether an input argument has desired "key: value" input or not.
- * borrowed from UnsafeDeserialization module with some changes
- */
-predicate checkkeyBalue(CfgNodes::ExprNodes::PairCfgNode p, string key, string value) {
- p.getKey().getConstantValue().isStringlikeValue(key) and
- DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().toString() = value
-}
-
/**
* An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
* the default), considered a sink for unsafe deserialization.
- * borrowed from UnsafeDeserialization module with some changes
*/
class UnsafePlistParsexmlArgument extends PlistUnsafeSinks {
UnsafePlistParsexmlArgument() {
- exists(DataFlow::CallNode plistParsexml |
- plistParsexml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
+ exists(DataFlow::CallNode plistParseXml |
+ plistParseXml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
|
- this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
- // Exclude calls that explicitly pass a safe mode option.
- checkkeyBalue(plistParsexml.getArgument(1).asExpr(), "marshal", "true")
+ this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
+ plistParseXml.getKeywordArgument("marshal").getConstantValue().isBoolean(true)
or
- this = [plistParsexml.getArgument(0), plistParsexml.getKeywordArgument("filename_or_xml")] and
- plistParsexml.getNumberOfArguments() = 1
+ this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
+ plistParseXml.getNumberOfArguments() = 1
)
}
}
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
index 63f64b5cd22..433873d6fa0 100644
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
+++ b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
@@ -1,12 +1,16 @@
-require 'yaml'
+require 'plist'
class UsersController < ActionController::Base
def example
# not safe
+ config = true
result = Plist.parse_xml(params[:yaml_string])
+ result = Plist.parse_xml(params[:yaml_string], marshal: config)
result = Plist.parse_xml(params[:yaml_string], marshal: true)
# safe
+ config = false
result = Plist.parse_xml(params[:yaml_string], marshal: false)
+ result = Plist.parse_xml(params[:yaml_string], marshal: config)
end
end
diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
index 406ba24935b..9ee9234770e 100644
--- a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
+++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
@@ -17,6 +17,17 @@ libraries that support it, such as the Ruby standard library's JSON
module, ensure that the parser is configured to disable
deserialization of arbitrary objects.
+
+
+YAML/Psych recommendation:
+After Psych(YAML) 4.0.0, the load method is same as safe_load method.
+This vulnerability can be prevented by using YAML.load (same as YAML.safe_load), YAML.load_file (same as YAML.safe_load_file) instead of YAML.unsafe_* methods.
+Be careful that YAML.load_stream don't use safe_load method, Also Be careful the to_ruby method of Psych get called on a trusted parsed (YAML.parse*) yaml document.
+
+
+
+This vulnerability in Plist can be prevented by calling Plist.parse_xml FileOrXmlString, marshal: false.
+
@@ -27,6 +38,14 @@ on data from an HTTP request. Since these methods are capable of deserializing
to arbitrary objects, this is inherently unsafe.
+
+
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
+
+
+
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
+
+
+
Using JSON.parse and YAML.safe_load instead, as in the
following example, removes the vulnerability. Similarly, calling
@@ -55,6 +74,10 @@ Ruby documentation: security guidance on the YAML library.
+
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
-
+
diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
index 9ee9234770e..1ebf32750a1 100644
--- a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
+++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
@@ -40,11 +40,11 @@ to arbitrary objects, this is inherently unsafe.
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
-
+
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
-
+
Using JSON.parse and YAML.safe_load instead, as in the
From d727d573d5ce139e8356bfca8765fc4dbcc9caa4 Mon Sep 17 00:00:00 2001
From: amammad
Date: Thu, 27 Apr 2023 06:48:15 +0200
Subject: [PATCH 164/813] v4.2 write exact version of yaml.load default loader
change
---
.../ruby/security/UnsafeDeserializationCustomizations.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index fcaceed1b3a..9dea66252e5 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -79,7 +79,7 @@ module UnsafeDeserialization {
* for unsafe deserialization. The `YAML` module is an alias of `Psych` in
* recent versions of Ruby.
* the `this = yamlNode().getAMethodCall("load").getArgument(0)` is safe
- * in recent versions of YAML library, so it will be removed in future.
+ * in psych/yaml library after [v4.0.0](https://github.com/ruby/psych/releases/tag/v4.0.0), so it will be removed in future.
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
From b8c3cba4ff0fa9819982425cca1bf41dcbd0e2c0 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Fri, 26 May 2023 14:48:16 +0000
Subject: [PATCH 165/813] Ruby: Consolidate unsafe deserialization queries
Merge the experimental YAMLUnsafeDeserialization and
PlistUnsafeDeserialization queries into the generate
UnsafeDeserialization query in the default suite.
These queries look for some specific sinks that we now find in the
general query.
Also apply some small code and comment refactors.
---
ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll | 34 +++----
.../UnsafeDeserializationCustomizations.qll | 25 +++---
.../cwe-502/PlistUnsafeDeserialization.qhelp | 24 -----
.../cwe-502/PlistUnsafeDeserialization.ql | 57 ------------
.../cwe-502/PlistUnsafeDeserialization.rb | 17 ----
.../cwe-502/YAMLUnsafeDeserialization.qhelp | 26 ------
.../cwe-502/YAMLUnsafeDeserialization.ql | 88 -------------------
.../cwe-502/UnsafeDeserialization.qhelp | 22 +++--
.../PlistUnsafeDeserialization.expected | 12 ---
.../cwe-502/PlistUnsafeDeserialization.qlref | 1 -
.../YAMLUnsafeDeserialization.expected | 34 -------
.../cwe-502/YAMLUnsafeDeserialization.qlref | 1 -
.../cwe-502/YAMLUnsafeDeserialization.rb | 22 -----
.../PlistUnsafeDeserialization.rb | 0
.../UnsafeDeserialization.expected | 42 +++++++++
.../YAMLUnsafeDeserialization.rb | 0
16 files changed, 75 insertions(+), 330 deletions(-)
delete mode 100644 ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
delete mode 100644 ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
delete mode 100644 ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
delete mode 100644 ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp
delete mode 100644 ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
delete mode 100644 ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb
rename ruby/ql/test/query-tests/{experimental/Security/cwe-502 => security/cwe-502/unsafe-deserialization}/PlistUnsafeDeserialization.rb (100%)
rename ruby/ql/{src/experimental/cwe-502 => test/query-tests/security/cwe-502/unsafe-deserialization}/YAMLUnsafeDeserialization.rb (100%)
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
index 81afdcbed7b..65596df7fe2 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/Yaml.qll
@@ -8,36 +8,28 @@ private import codeql.ruby.ApiGraphs
/**
* A taint step related to the result of `YAML.parse` calls, or similar.
- *In the following example, this step will propagate taint from
- *`source` to `sink`:
+ * In the following example, this step will propagate taint from
+ * `source` to `sink`:
*
- *```rb
- *x = source
- *result = YAML.parse(x)
- *sink result.to_ruby # Unsafe call
+ * ```rb
+ * x = source
+ * result = YAML.parse(x)
+ * sink result.to_ruby # Unsafe call
* ```
*/
private class YamlParseStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode yamlParserMethod |
- yamlParserMethod = yamlNode().getAMethodCall(["parse", "parse_stream"]) and
+ succ = yamlParserMethod.getAMethodCall("to_ruby") and
(
- pred = yamlParserMethod.getArgument(0) or
- pred = yamlParserMethod.getKeywordArgument("yaml")
- ) and
- succ = yamlParserMethod.getAMethodCall("to_ruby")
- or
- yamlParserMethod = yamlNode().getAMethodCall("parse_file") and
- (
- pred = yamlParserMethod.getArgument(0) or
- pred = yamlParserMethod.getKeywordArgument("filename")
- ) and
- succ = yamlParserMethod.getAMethodCall("to_ruby")
+ yamlParserMethod = yamlNode().getAMethodCall(["parse", "parse_stream"]) and
+ pred = [yamlParserMethod.getArgument(0), yamlParserMethod.getKeywordArgument("yaml")]
+ or
+ yamlParserMethod = yamlNode().getAMethodCall("parse_file") and
+ pred = [yamlParserMethod.getArgument(0), yamlParserMethod.getKeywordArgument("filename")]
+ )
)
}
}
-/**
- * YAML/Psych Top level Class member
- */
private API::Node yamlNode() { result = API::getTopLevelMember(["YAML", "Psych"]) }
diff --git a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
index 9dea66252e5..e38a38af2dc 100644
--- a/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
+++ b/ruby/ql/lib/codeql/ruby/security/UnsafeDeserializationCustomizations.qll
@@ -75,14 +75,13 @@ module UnsafeDeserialization {
}
/**
- * An argument in a call to `YAML.unsafe_*` and `YAML.load_stream` , considered sinks
+ * An argument in a call to `YAML.unsafe_*` and `YAML.load_stream` , considered a sink
* for unsafe deserialization. The `YAML` module is an alias of `Psych` in
* recent versions of Ruby.
- * the `this = yamlNode().getAMethodCall("load").getArgument(0)` is safe
- * in psych/yaml library after [v4.0.0](https://github.com/ruby/psych/releases/tag/v4.0.0), so it will be removed in future.
*/
class YamlLoadArgument extends Sink {
YamlLoadArgument() {
+ // Note: this is safe in psych/yaml >= 4.0.0.
this = yamlNode().getAMethodCall("load").getArgument(0)
or
this =
@@ -94,16 +93,11 @@ module UnsafeDeserialization {
}
}
- /**
- * YAML/Psych Top level Class member
- */
private API::Node yamlNode() { result = API::getTopLevelMember(["YAML", "Psych"]) }
/**
- * An argument in a call to `YAML.parse*`, considered sinks
- * for unsafe deserialization if there is a call to `to_ruby` on returned value of them,
- * so this need some additional taint steps. The `YAML` module is an alias of `Psych` in
- * recent versions of Ruby.
+ * An argument in a call to `YAML.parse*`, considered a sink for unsafe deserialization
+ * if there is a call to `to_ruby` on the returned value.
*/
class YamlParseArgument extends Sink {
YamlParseArgument() {
@@ -237,7 +231,7 @@ module UnsafeDeserialization {
}
/**
- * An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
+ * An argument in a call to `Plist.parse_xml` where `marshal` is `true` (which is
* the default), considered a sink for unsafe deserialization.
*/
class UnsafePlistParsexmlArgument extends Sink {
@@ -246,10 +240,11 @@ module UnsafeDeserialization {
plistParseXml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
|
this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
- plistParseXml.getKeywordArgument("marshal").getConstantValue().isBoolean(true)
- or
- this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
- plistParseXml.getNumberOfArguments() = 1
+ (
+ plistParseXml.getKeywordArgument("marshal").getConstantValue().isBoolean(true)
+ or
+ plistParseXml.getNumberOfArguments() = 1
+ )
)
}
}
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
deleted file mode 100644
index 9863402d583..00000000000
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.qhelp
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- Processing an unvalidated user input can allow an attacker to execute arbitrary code in your application.
- Unsafe deserializing the malicious serialized xml document through the Plist library, making it possible to execute some code or execute arbitrary code with the help of a complete gadget chain.
-
-
-
-
- This vulnerability in Plist can be prevented by calling Plist.parse_xml FileOrXmlString, marshal: false.
-
-
-
-
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
-
-
-
\ No newline at end of file
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
deleted file mode 100644
index 4ba55824598..00000000000
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.ql
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * @name Unsafe Deserialization of user-controlled data by Plist
- * @description Deserializing user-controlled data may allow attackers to
- * execute arbitrary code.
- * @kind path-problem
- * @problem.severity warning
- * @security-severity 9.8
- * @precision high
- * @id rb/plist-unsafe-deserialization
- * @tags security
- * experimental
- * external/cwe/cwe-502
- */
-
-import codeql.ruby.ApiGraphs
-import codeql.ruby.DataFlow
-import codeql.ruby.TaintTracking
-import codeql.ruby.CFG
-import DataFlow::PathGraph
-import codeql.ruby.security.UnsafeDeserializationCustomizations
-
-abstract class PlistUnsafeSinks extends DataFlow::Node { }
-
-/**
- * An argument in a call to `Plist.parse_xml` where the marshal is `true` (which is
- * the default), considered a sink for unsafe deserialization.
- */
-class UnsafePlistParsexmlArgument extends PlistUnsafeSinks {
- UnsafePlistParsexmlArgument() {
- exists(DataFlow::CallNode plistParseXml |
- plistParseXml = API::getTopLevelMember("Plist").getAMethodCall("parse_xml")
- |
- this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
- plistParseXml.getKeywordArgument("marshal").getConstantValue().isBoolean(true)
- or
- this = [plistParseXml.getArgument(0), plistParseXml.getKeywordArgument("filename_or_xml")] and
- plistParseXml.getNumberOfArguments() = 1
- )
- }
-}
-
-class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "PlistUnsafeDeserialization" }
-
- override predicate isSource(DataFlow::Node source) {
- // to detect CVE-2021-33575, we should uncomment following line instead of current UnsafeDeserialization::Source
- // source instanceof DataFlow::LocalSourceNode
- source instanceof UnsafeDeserialization::Source
- }
-
- override predicate isSink(DataFlow::Node sink) { sink instanceof PlistUnsafeSinks }
-}
-
-from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb b/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
deleted file mode 100644
index 433873d6fa0..00000000000
--- a/ruby/ql/src/experimental/cwe-502/PlistUnsafeDeserialization.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'plist'
-class UsersController < ActionController::Base
- def example
- # not safe
- config = true
- result = Plist.parse_xml(params[:yaml_string])
- result = Plist.parse_xml(params[:yaml_string], marshal: config)
- result = Plist.parse_xml(params[:yaml_string], marshal: true)
-
- # safe
- config = false
- result = Plist.parse_xml(params[:yaml_string], marshal: false)
- result = Plist.parse_xml(params[:yaml_string], marshal: config)
- end
-end
-
-
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp
deleted file mode 100644
index c644f495271..00000000000
--- a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.qhelp
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
- Processing an unvalidated user input can allow an attacker to execute arbitrary code in your application.
- Unsafe deserializing the malicious serialized yaml document through the Psych (YAML) library, making it possible to execute some code or execute arbitrary code with the help of a complete gadget chain.
-
-
-
-
- After Psych(YAML) 4.0.0, the load method is same as safe_load method.
- This vulnerability can be prevented by using YAML.load (same as YAML.safe_load), YAML.load_file (same as YAML.safe_load_file) instead of YAML.unsafe_* methods.
- Be careful that YAML.load_stream don't use safe_load method, Also Be careful the to_ruby method of Psych get called on a trusted parsed (YAML.parse*) yaml document.
-
-
-
-
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
-
-
\ No newline at end of file
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql b/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql
deleted file mode 100644
index 5cb720d19da..00000000000
--- a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.ql
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * @name Unsafe Deserialization of user-controlled data by YAML
- * @description Deserializing user-controlled data may allow attackers to
- * execute arbitrary code.
- * @kind path-problem
- * @problem.severity warning
- * @security-severity 9.8
- * @precision high
- * @id rb/YAML-unsafe-deserialization
- * @tags security
- * experimental
- * external/cwe/cwe-502
- */
-
-import codeql.ruby.ApiGraphs
-import codeql.ruby.DataFlow
-import codeql.ruby.TaintTracking
-import DataFlow::PathGraph
-import codeql.ruby.security.UnsafeDeserializationCustomizations
-
-abstract class YamlUnsafeSinks extends DataFlow::Node { }
-
-class YamlUnsafeArgument extends YamlUnsafeSinks {
- YamlUnsafeArgument() {
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["unsafe_load_file", "unsafe_load", "load_stream"])
- .getArgument(0)
- or
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["unsafe_load", "load_stream"])
- .getKeywordArgument("yaml")
- or
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall("unsafe_load_file")
- .getKeywordArgument("filename")
- or
- this =
- API::getTopLevelMember(["YAML", "Psych"])
- .getAMethodCall(["parse", "parse_stream", "parse_file"])
- .getAMethodCall("to_ruby")
- }
-}
-
-class Configuration extends TaintTracking::Configuration {
- Configuration() { this = "YamlUnsafeDeserialization" }
-
- override predicate isSource(DataFlow::Node source) {
- // to detect CVE-2022-32224, we should uncomment following line instead of current UnsafeDeserialization::Source
- // source instanceof DataFlow::LocalSourceNode
- source instanceof UnsafeDeserialization::Source
- }
-
- override predicate isSink(DataFlow::Node sink) {
- // after changing the isSource for detecting CVE-2022-32224
- // uncomment the following line only see the CVE sink not other files similar sinks
- // sink.getLocation().getFile().toString().matches("%yaml_column%") and
- sink instanceof YamlUnsafeSinks
- }
-
- override predicate isAdditionalTaintStep(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods =
- API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall(["parse", "parse_stream"]) and
- (
- nodeFrom = yaml_parser_methods.getArgument(0) or
- nodeFrom = yaml_parser_methods.getKeywordArgument("yaml")
- ) and
- nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
- )
- or
- exists(DataFlow::CallNode yaml_parser_methods |
- yaml_parser_methods = API::getTopLevelMember(["YAML", "Psych"]).getAMethodCall("parse_file") and
- (
- nodeFrom = yaml_parser_methods.getArgument(0) or
- nodeFrom = yaml_parser_methods.getKeywordArgument("filename")
- ) and
- nodeTo = yaml_parser_methods.getAMethodCall("to_ruby")
- )
- }
-}
-
-from Configuration config, DataFlow::PathNode source, DataFlow::PathNode sink
-where config.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "Unsafe deserialization depends on a $@.", source.getNode(),
- "potentially untrusted source"
diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
index 1ebf32750a1..17f03853570 100644
--- a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
+++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
@@ -19,14 +19,19 @@ deserialization of arbitrary objects.
-YAML/Psych recommendation:
-After Psych(YAML) 4.0.0, the load method is same as safe_load method.
-This vulnerability can be prevented by using YAML.load (same as YAML.safe_load), YAML.load_file (same as YAML.safe_load_file) instead of YAML.unsafe_* methods.
-Be careful that YAML.load_stream don't use safe_load method, Also Be careful the to_ruby method of Psych get called on a trusted parsed (YAML.parse*) yaml document.
+If deserializing an untrusted YAML document using the psych gem
+prior to version 4.0.0, the load method is vulnerable. Use
+safe_load instead. With psych version 4.0.0 and later,
+the load is safe. The same applies to load_file.
+load_stream is vulnerable in all versions. The safe versions of these
+methods (safe_load and safe_load_file) are not vulnerable
+in any known version.
-This vulnerability in Plist can be prevented by calling Plist.parse_xml FileOrXmlString, marshal: false.
+To safely deserialize Property List
+files using the plist gem, ensure that you pass marshal: false
+when calling Plist.parse_xml.
@@ -39,13 +44,6 @@ to arbitrary objects, this is inherently unsafe.
-
In the example below, you can see safe and unsafe methods get called by a remote user input. You can give correct authorization to users, or you can use safe methods for loading yaml documents.
-
-
-
In the example below, you can see safe and unsafe Plist dangerous method calls that can be abused by a remote user input. You can use "marshal: false" as an arugument for Plist.parse_xml to use it safe.
-
-
-
Using JSON.parse and YAML.safe_load instead, as in the
following example, removes the vulnerability. Similarly, calling
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
deleted file mode 100644
index 967ef978a3b..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.expected
+++ /dev/null
@@ -1,12 +0,0 @@
-edges
-| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] |
-| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] |
-nodes
-| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | semmle.label | call to params : |
-| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | semmle.label | ...[...] |
-| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | semmle.label | call to params : |
-| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | semmle.label | ...[...] |
-subpaths
-#select
-| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params : | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | potentially untrusted source |
-| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params : | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
deleted file mode 100644
index f7bfbada7b5..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/cwe-502/PlistUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
deleted file mode 100644
index 9a9bd05e514..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.expected
+++ /dev/null
@@ -1,34 +0,0 @@
-edges
-| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] |
-| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] |
-| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] |
-| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : |
-| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby |
-| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : |
-| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby |
-| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : |
-| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby |
-nodes
-| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
-| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] : | semmle.label | ...[...] : |
-| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
-| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | semmle.label | call to params : |
-| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] : | semmle.label | ...[...] : |
-subpaths
-#select
-| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params : | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | potentially untrusted source |
-| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params : | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | potentially untrusted source |
-| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params : | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | potentially untrusted source |
-| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params : | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | potentially untrusted source |
-| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params : | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | potentially untrusted source |
-| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params : | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | potentially untrusted source |
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
deleted file mode 100644
index e06e0921159..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.qlref
+++ /dev/null
@@ -1 +0,0 @@
-experimental/cwe-502/YAMLUnsafeDeserialization.ql
\ No newline at end of file
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb b/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb
deleted file mode 100644
index 6e836a0a049..00000000000
--- a/ruby/ql/test/query-tests/experimental/Security/cwe-502/YAMLUnsafeDeserialization.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'yaml'
-class UsersController < ActionController::Base
- def example
- # safe
- Psych.load(params[:yaml_string])
- Psych.load_file(params[:yaml_file])
- Psych.parse_stream(params[:yaml_string])
- Psych.parse(params[:yaml_string])
- Psych.parse_file(params[:yaml_file])
- # unsafe
- Psych.unsafe_load(params[:yaml_string])
- Psych.unsafe_load_file(params[:yaml_file])
- Psych.load_stream(params[:yaml_string])
- parse_output = Psych.parse_stream(params[:yaml_string])
- parse_output.to_ruby
- Psych.parse(params[:yaml_string]).to_ruby
- Psych.parse_file(params[:yaml_file]).to_ruby
-
- end
-end
-
-
diff --git a/ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/PlistUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/test/query-tests/experimental/Security/cwe-502/PlistUnsafeDeserialization.rb
rename to ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/PlistUnsafeDeserialization.rb
diff --git a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
index 6fbbb14ef8a..b1f1d701a73 100644
--- a/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
+++ b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/UnsafeDeserialization.expected
@@ -1,4 +1,6 @@
edges
+| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] |
+| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] |
| UnsafeDeserialization.rb:10:5:10:19 | serialized_data | UnsafeDeserialization.rb:11:27:11:41 | serialized_data |
| UnsafeDeserialization.rb:10:23:10:50 | call to decode64 | UnsafeDeserialization.rb:10:5:10:19 | serialized_data |
| UnsafeDeserialization.rb:10:39:10:44 | call to params | UnsafeDeserialization.rb:10:39:10:50 | ...[...] |
@@ -29,7 +31,21 @@ edges
| UnsafeDeserialization.rb:87:5:87:13 | yaml_data | UnsafeDeserialization.rb:88:25:88:33 | yaml_data |
| UnsafeDeserialization.rb:87:17:87:22 | call to params | UnsafeDeserialization.rb:87:17:87:28 | ...[...] |
| UnsafeDeserialization.rb:87:17:87:28 | ...[...] | UnsafeDeserialization.rb:87:5:87:13 | yaml_data |
+| YAMLUnsafeDeserialization.rb:5:16:5:21 | call to params | YAMLUnsafeDeserialization.rb:5:16:5:35 | ...[...] |
+| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] |
+| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] |
+| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] |
+| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] |
+| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] |
+| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] |
+| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby |
nodes
+| PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | semmle.label | call to params |
+| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | semmle.label | ...[...] |
+| PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | semmle.label | call to params |
+| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | semmle.label | ...[...] |
| UnsafeDeserialization.rb:10:5:10:19 | serialized_data | semmle.label | serialized_data |
| UnsafeDeserialization.rb:10:23:10:50 | call to decode64 | semmle.label | call to decode64 |
| UnsafeDeserialization.rb:10:39:10:44 | call to params | semmle.label | call to params |
@@ -74,8 +90,27 @@ nodes
| UnsafeDeserialization.rb:98:24:98:32 | call to read | semmle.label | call to read |
| UnsafeDeserialization.rb:101:24:101:27 | call to gets | semmle.label | call to gets |
| UnsafeDeserialization.rb:104:24:104:32 | call to readlines | semmle.label | call to readlines |
+| YAMLUnsafeDeserialization.rb:5:16:5:21 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:5:16:5:35 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:14:39:14:58 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:16:17:16:36 | ...[...] | semmle.label | ...[...] |
+| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | semmle.label | call to to_ruby |
+| YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | semmle.label | call to params |
+| YAMLUnsafeDeserialization.rb:17:22:17:39 | ...[...] | semmle.label | ...[...] |
subpaths
#select
+| PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | PlistUnsafeDeserialization.rb:5:30:5:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:5:30:5:35 | call to params | user-provided value |
+| PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | PlistUnsafeDeserialization.rb:6:30:6:49 | ...[...] | Unsafe deserialization depends on a $@. | PlistUnsafeDeserialization.rb:6:30:6:35 | call to params | user-provided value |
| UnsafeDeserialization.rb:11:27:11:41 | serialized_data | UnsafeDeserialization.rb:10:39:10:44 | call to params | UnsafeDeserialization.rb:11:27:11:41 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:10:39:10:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:17:30:17:44 | serialized_data | UnsafeDeserialization.rb:16:39:16:44 | call to params | UnsafeDeserialization.rb:17:30:17:44 | serialized_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:16:39:16:44 | call to params | user-provided value |
| UnsafeDeserialization.rb:23:24:23:32 | json_data | UnsafeDeserialization.rb:22:17:22:22 | call to params | UnsafeDeserialization.rb:23:24:23:32 | json_data | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:22:17:22:22 | call to params | user-provided value |
@@ -91,3 +126,10 @@ subpaths
| UnsafeDeserialization.rb:98:24:98:32 | call to read | UnsafeDeserialization.rb:98:24:98:32 | call to read | UnsafeDeserialization.rb:98:24:98:32 | call to read | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:98:24:98:32 | call to read | value from stdin |
| UnsafeDeserialization.rb:101:24:101:27 | call to gets | UnsafeDeserialization.rb:101:24:101:27 | call to gets | UnsafeDeserialization.rb:101:24:101:27 | call to gets | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:101:24:101:27 | call to gets | value from stdin |
| UnsafeDeserialization.rb:104:24:104:32 | call to readlines | UnsafeDeserialization.rb:104:24:104:32 | call to readlines | UnsafeDeserialization.rb:104:24:104:32 | call to readlines | Unsafe deserialization depends on a $@. | UnsafeDeserialization.rb:104:24:104:32 | call to readlines | value from stdin |
+| YAMLUnsafeDeserialization.rb:5:16:5:35 | ...[...] | YAMLUnsafeDeserialization.rb:5:16:5:21 | call to params | YAMLUnsafeDeserialization.rb:5:16:5:35 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:5:16:5:21 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | YAMLUnsafeDeserialization.rb:11:23:11:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:11:23:11:28 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | YAMLUnsafeDeserialization.rb:12:28:12:45 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:12:28:12:33 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | YAMLUnsafeDeserialization.rb:13:23:13:42 | ...[...] | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:13:23:13:28 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | YAMLUnsafeDeserialization.rb:15:5:15:24 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:14:39:14:44 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | YAMLUnsafeDeserialization.rb:16:5:16:45 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:16:17:16:22 | call to params | user-provided value |
+| YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | YAMLUnsafeDeserialization.rb:17:5:17:48 | call to to_ruby | Unsafe deserialization depends on a $@. | YAMLUnsafeDeserialization.rb:17:22:17:27 | call to params | user-provided value |
diff --git a/ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.rb b/ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/YAMLUnsafeDeserialization.rb
similarity index 100%
rename from ruby/ql/src/experimental/cwe-502/YAMLUnsafeDeserialization.rb
rename to ruby/ql/test/query-tests/security/cwe-502/unsafe-deserialization/YAMLUnsafeDeserialization.rb
From 562065f29e6fd387534e517942e89f907117a290 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Sat, 27 May 2023 00:59:36 +0000
Subject: [PATCH 166/813] Ruby: Add change note
---
ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md
diff --git a/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md b/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md
new file mode 100644
index 00000000000..4039e7c90dc
--- /dev/null
+++ b/ruby/ql/lib/change-notes/2023-05-27-unsafe-deserialization.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Additional sinks for `rb/unsafe-deserialization` have been added. This includes various methods from the `yaml` and `plist` gems, which deserialize YAML and Property List data, respectively.
From e515981c81d1fd82d586de9bc4afa8f5e4988470 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Sat, 27 May 2023 12:01:00 +0000
Subject: [PATCH 167/813] Ruby: Remove unused examples
---
.../examples/PlistUnsafeDeserialization.rb | 17 --------------
.../examples/YAMLUnsafeDeserialization.rb | 22 -------------------
2 files changed, 39 deletions(-)
delete mode 100644 ruby/ql/src/queries/security/cwe-502/examples/PlistUnsafeDeserialization.rb
delete mode 100644 ruby/ql/src/queries/security/cwe-502/examples/YAMLUnsafeDeserialization.rb
diff --git a/ruby/ql/src/queries/security/cwe-502/examples/PlistUnsafeDeserialization.rb b/ruby/ql/src/queries/security/cwe-502/examples/PlistUnsafeDeserialization.rb
deleted file mode 100644
index 433873d6fa0..00000000000
--- a/ruby/ql/src/queries/security/cwe-502/examples/PlistUnsafeDeserialization.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'plist'
-class UsersController < ActionController::Base
- def example
- # not safe
- config = true
- result = Plist.parse_xml(params[:yaml_string])
- result = Plist.parse_xml(params[:yaml_string], marshal: config)
- result = Plist.parse_xml(params[:yaml_string], marshal: true)
-
- # safe
- config = false
- result = Plist.parse_xml(params[:yaml_string], marshal: false)
- result = Plist.parse_xml(params[:yaml_string], marshal: config)
- end
-end
-
-
diff --git a/ruby/ql/src/queries/security/cwe-502/examples/YAMLUnsafeDeserialization.rb b/ruby/ql/src/queries/security/cwe-502/examples/YAMLUnsafeDeserialization.rb
deleted file mode 100644
index 6e836a0a049..00000000000
--- a/ruby/ql/src/queries/security/cwe-502/examples/YAMLUnsafeDeserialization.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-require 'yaml'
-class UsersController < ActionController::Base
- def example
- # safe
- Psych.load(params[:yaml_string])
- Psych.load_file(params[:yaml_file])
- Psych.parse_stream(params[:yaml_string])
- Psych.parse(params[:yaml_string])
- Psych.parse_file(params[:yaml_file])
- # unsafe
- Psych.unsafe_load(params[:yaml_string])
- Psych.unsafe_load_file(params[:yaml_file])
- Psych.load_stream(params[:yaml_string])
- parse_output = Psych.parse_stream(params[:yaml_string])
- parse_output.to_ruby
- Psych.parse(params[:yaml_string]).to_ruby
- Psych.parse_file(params[:yaml_file]).to_ruby
-
- end
-end
-
-
From ca1024e2858c6f541c5adef4e0405dbc6b7625f1 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Mon, 29 May 2023 03:46:30 +0000
Subject: [PATCH 168/813] Ruby: Reword unsafe deserialization qhelp
---
.../security/cwe-502/UnsafeDeserialization.qhelp | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
index 17f03853570..8bacb266423 100644
--- a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
+++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
@@ -19,13 +19,12 @@ deserialization of arbitrary objects.
-If deserializing an untrusted YAML document using the psych gem
-prior to version 4.0.0, the load method is vulnerable. Use
-safe_load instead. With psych version 4.0.0 and later,
-the load is safe. The same applies to load_file.
-load_stream is vulnerable in all versions. The safe versions of these
-methods (safe_load and safe_load_file) are not vulnerable
-in any known version.
+If deserializing an untrusted YAML document using the psych gem,
+prefer the safe_load and safe_load_file methods over
+load and load_file, as the former will safely
+handle untrusted data. Avoid passing untrusted data to the load_stream
+method. In psych version 4.0.0 and above, the load can
+safely be used.
From e70e3e52dcf7456465fdc6f59e687f08293c40d2 Mon Sep 17 00:00:00 2001
From: Harry Maclean
Date: Mon, 29 May 2023 04:05:42 +0000
Subject: [PATCH 169/813] Ruby: fix typo in qhelp
---
.../ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
index 8bacb266423..e361b62338e 100644
--- a/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
+++ b/ruby/ql/src/queries/security/cwe-502/UnsafeDeserialization.qhelp
@@ -23,7 +23,7 @@ If deserializing an untrusted YAML document using the psych gem,
prefer the safe_load and safe_load_file methods over
load and load_file, as the former will safely
handle untrusted data. Avoid passing untrusted data to the load_stream
-method. In psych version 4.0.0 and above, the load can
+method. In psych version 4.0.0 and above, the load method can
safely be used.
From 6386ef3b962cb6a16c9c09f4842a1383982a18da Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Mon, 29 May 2023 09:58:52 +0200
Subject: [PATCH 170/813] Further perf improvements
---
java/ql/src/utils/stub-generator/Stubs.qll | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/java/ql/src/utils/stub-generator/Stubs.qll b/java/ql/src/utils/stub-generator/Stubs.qll
index a42a806455e..889bc6b466c 100644
--- a/java/ql/src/utils/stub-generator/Stubs.qll
+++ b/java/ql/src/utils/stub-generator/Stubs.qll
@@ -287,18 +287,23 @@ private string stubQualifier(RefType t) {
pragma[nomagic]
private predicate needsPackageNameHelper(RefType t, GeneratedTopLevel top, string name) {
- t.getSourceDeclaration() = [getAReferencedType(top), top].getSourceDeclaration() and
+ t.getSourceDeclaration() =
+ pragma[only_bind_out]([getAReferencedType(top), top].getSourceDeclaration()) and
name = t.getName()
}
+pragma[nomagic]
+private predicate describesMultipleTypes(GeneratedTopLevel top, string name) {
+ 2 <= strictcount(RefType t | needsPackageNameHelper(t, top, name))
+}
+
/**
* Holds if `t` may clash with another type of the same name, so should be referred to using the fully qualified name
*/
private predicate needsPackageName(RefType t) {
- exists(GeneratedTopLevel top, RefType other, string name |
+ exists(GeneratedTopLevel top, string name |
needsPackageNameHelper(t, top, name) and
- needsPackageNameHelper(other, top, name) and
- t != other
+ describesMultipleTypes(top, name)
)
}
From 2d81e30d8191fcb749de6d3e95ffd3536eba674d Mon Sep 17 00:00:00 2001
From: Andrew Eisenberg
Date: Mon, 29 May 2023 13:45:41 -0700
Subject: [PATCH 171/813] Fix `addsTo.pack` references
This change is a prerequisite for a CLI change where there will be
strict testing of the `addsTo.pack` values. It must resolve to a pack
reference that is a transitive dependency of the current query's pack.
---
.../lib/ext/org.apache.hc.core5.http.impl.bootstrap.model.yml | 2 +-
.../src/utils/flowtestcasegenerator/GenerateFlowTestCase.py | 4 ++--
java/ql/test/ext/TestModels/test.ext.yml | 2 +-
.../kotlin/library-tests/dataflow/notnullexpr/test.ext.yml | 2 +-
.../test/kotlin/library-tests/dataflow/whenexpr/test.ext.yml | 2 +-
.../library-tests/dataflow/callback-dispatch/test.ext.yml | 3 +--
.../library-tests/dataflow/collections/containerflow.ext.yml | 2 +-
.../test/library-tests/dataflow/external-models/sinks.ext.yml | 2 +-
.../test/library-tests/dataflow/external-models/srcs.ext.yml | 2 +-
.../test/library-tests/dataflow/external-models/steps.ext.yml | 2 +-
java/ql/test/library-tests/dataflow/synth-global/test.ext.yml | 2 +-
.../android/content-provider-summaries/test.ext.yml | 2 +-
.../test/library-tests/frameworks/android/intent/test.ext.yml | 2 +-
.../frameworks/android/notification/test.ext.yml | 2 +-
.../library-tests/frameworks/apache-collections/test.ext.yml | 2 +-
.../ql/test/library-tests/frameworks/apache-http/flow.ext.yml | 2 +-
.../frameworks/guava/generated/collect/test.ext.yml | 2 +-
.../ql/test/library-tests/frameworks/jdk/java.io/test.ext.yml | 2 +-
.../library-tests/frameworks/netty/generated/test.ext.yml | 2 +-
java/ql/test/library-tests/frameworks/stream/test.ext.yml | 2 +-
java/ql/test/library-tests/optional/test.ext.yml | 2 +-
21 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/java/ql/lib/ext/org.apache.hc.core5.http.impl.bootstrap.model.yml b/java/ql/lib/ext/org.apache.hc.core5.http.impl.bootstrap.model.yml
index a2789520908..c9515372645 100644
--- a/java/ql/lib/ext/org.apache.hc.core5.http.impl.bootstrap.model.yml
+++ b/java/ql/lib/ext/org.apache.hc.core5.http.impl.bootstrap.model.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: sinkModel
data:
- ["org.apache.hc.core5.http.impl.bootstrap", "HttpAsyncRequester", True, "connect", "(HttpHost,Timeout)", "", "Argument[0]", "open-url", "hq-manual"]
diff --git a/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py b/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py
index 5e35ca52dd1..1cc943a78ec 100755
--- a/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py
+++ b/java/ql/src/utils/flowtestcasegenerator/GenerateFlowTestCase.py
@@ -18,7 +18,7 @@ GenerateFlowTestCase.py specsToTest projectPom.xml outdir [--force]
This generates test cases exercising function model specifications found in specsToTest
producing files Test.java, test.ql, test.ext.yml and test.expected in outdir.
-specsToTest should either be a .csv file, a .yml file, or a directory of .yml files, containing the
+specsToTest should either be a .csv file, a .yml file, or a directory of .yml files, containing the
model specifications to test.
projectPom.xml should be a Maven pom sufficient to resolve the classes named in specsToTest.csv.
@@ -276,7 +276,7 @@ if len(supportModelRows) != 0:
modelSpecRow[0].strip() for modelSpecRow in supportModelRows)
dataextensions = f"""extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
{models}
diff --git a/java/ql/test/ext/TestModels/test.ext.yml b/java/ql/test/ext/TestModels/test.ext.yml
index 4fff7d575a3..c5873214f71 100644
--- a/java/ql/test/ext/TestModels/test.ext.yml
+++ b/java/ql/test/ext/TestModels/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newWithMapValueDefault", "(Object)", "", "Argument[0]", "ReturnValue.MapValue", "value", "manual"]
diff --git a/java/ql/test/kotlin/library-tests/dataflow/notnullexpr/test.ext.yml b/java/ql/test/kotlin/library-tests/dataflow/notnullexpr/test.ext.yml
index 589c787bf9a..700f3f51e6f 100644
--- a/java/ql/test/kotlin/library-tests/dataflow/notnullexpr/test.ext.yml
+++ b/java/ql/test/kotlin/library-tests/dataflow/notnullexpr/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["", "Uri", False, "getQueryParameter", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/test/kotlin/library-tests/dataflow/whenexpr/test.ext.yml b/java/ql/test/kotlin/library-tests/dataflow/whenexpr/test.ext.yml
index 589c787bf9a..700f3f51e6f 100644
--- a/java/ql/test/kotlin/library-tests/dataflow/whenexpr/test.ext.yml
+++ b/java/ql/test/kotlin/library-tests/dataflow/whenexpr/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["", "Uri", False, "getQueryParameter", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/test/library-tests/dataflow/callback-dispatch/test.ext.yml b/java/ql/test/library-tests/dataflow/callback-dispatch/test.ext.yml
index 5f35c923ad0..a153e39a0e0 100644
--- a/java/ql/test/library-tests/dataflow/callback-dispatch/test.ext.yml
+++ b/java/ql/test/library-tests/dataflow/callback-dispatch/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["my.callback.qltest", "A", False, "applyConsumer1", "(Object,Consumer1)", "", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"]
@@ -15,4 +15,3 @@ extensions:
- ["my.callback.qltest", "A", False, "produceConsume", "(Producer1,Consumer3)", "", "Argument[1].Parameter[0]", "ReturnValue", "value", "manual"]
- ["my.callback.qltest", "A", False, "applyConverter1", "(Object,Converter1)", "", "Argument[0]", "Argument[1].Parameter[0]", "value", "manual"]
- ["my.callback.qltest", "A", False, "applyConverter1", "(Object,Converter1)", "", "Argument[1].ReturnValue", "ReturnValue", "value", "manual"]
-
diff --git a/java/ql/test/library-tests/dataflow/collections/containerflow.ext.yml b/java/ql/test/library-tests/dataflow/collections/containerflow.ext.yml
index ebe7e3b6ea5..c12a0156d0c 100644
--- a/java/ql/test/library-tests/dataflow/collections/containerflow.ext.yml
+++ b/java/ql/test/library-tests/dataflow/collections/containerflow.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["", "B", False, "readElement", "(Spliterator)", "", "Argument[0].Element", "ReturnValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/dataflow/external-models/sinks.ext.yml b/java/ql/test/library-tests/dataflow/external-models/sinks.ext.yml
index 55a76b79b21..d469a2de0dc 100644
--- a/java/ql/test/library-tests/dataflow/external-models/sinks.ext.yml
+++ b/java/ql/test/library-tests/dataflow/external-models/sinks.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: sinkModel
data:
- ["my.qltest", "B", False, "sink1", "(Object)", "", "Argument[0]", "qltest", "manual"]
diff --git a/java/ql/test/library-tests/dataflow/external-models/srcs.ext.yml b/java/ql/test/library-tests/dataflow/external-models/srcs.ext.yml
index 7730d41e549..9693152f1c0 100644
--- a/java/ql/test/library-tests/dataflow/external-models/srcs.ext.yml
+++ b/java/ql/test/library-tests/dataflow/external-models/srcs.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: sourceModel
data:
- ["my.qltest", "A", False, "src1", "()", "", "ReturnValue", "qltest", "manual"]
diff --git a/java/ql/test/library-tests/dataflow/external-models/steps.ext.yml b/java/ql/test/library-tests/dataflow/external-models/steps.ext.yml
index 41d26cf815a..c6a1fb69d6d 100644
--- a/java/ql/test/library-tests/dataflow/external-models/steps.ext.yml
+++ b/java/ql/test/library-tests/dataflow/external-models/steps.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["my.qltest", "C", False, "stepArgRes", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/test/library-tests/dataflow/synth-global/test.ext.yml b/java/ql/test/library-tests/dataflow/synth-global/test.ext.yml
index 3d3bbe9fd47..58b4d2ecc24 100644
--- a/java/ql/test/library-tests/dataflow/synth-global/test.ext.yml
+++ b/java/ql/test/library-tests/dataflow/synth-global/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["my.qltest.synth", "A", False, "storeInArray", "(String)", "", "Argument[0]", "SyntheticGlobal[db1].ArrayElement", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ext.yml b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ext.yml
index cf5c80bc456..06781456552 100644
--- a/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/android/content-provider-summaries/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newWithMapValueDefault", "(Object)", "", "Argument[0]", "ReturnValue.MapValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/android/intent/test.ext.yml b/java/ql/test/library-tests/frameworks/android/intent/test.ext.yml
index 31321102a46..0a3ce554bc7 100644
--- a/java/ql/test/library-tests/frameworks/android/intent/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/android/intent/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newBundleWithMapValue", "(Object)", "", "Argument[0]", "ReturnValue.MapValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/android/notification/test.ext.yml b/java/ql/test/library-tests/frameworks/android/notification/test.ext.yml
index bd5c804fddc..69b416a5b72 100644
--- a/java/ql/test/library-tests/frameworks/android/notification/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/android/notification/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "getMapKeyDefault", "(Bundle)", "", "Argument[0].MapKey", "ReturnValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/apache-collections/test.ext.yml b/java/ql/test/library-tests/frameworks/apache-collections/test.ext.yml
index a5d1cc8e1ab..60531154074 100644
--- a/java/ql/test/library-tests/frameworks/apache-collections/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/apache-collections/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newRBWithMapValue", "", "", "Argument[0]", "ReturnValue.MapValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/apache-http/flow.ext.yml b/java/ql/test/library-tests/frameworks/apache-http/flow.ext.yml
index ff32ab78646..6e41b8a4e24 100644
--- a/java/ql/test/library-tests/frameworks/apache-http/flow.ext.yml
+++ b/java/ql/test/library-tests/frameworks/apache-http/flow.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Client", False, "getURIBuilder_pathDefault", "(Object)", "", "Argument[0].SyntheticField[org.apache.http.client.utils.URIBuilder.path]", "ReturnValue", "taint", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ext.yml b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ext.yml
index 153b649a3e6..e711fa15ecc 100644
--- a/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/guava/generated/collect/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newWithElementDefault", "(Object)", "", "Argument[0]", "ReturnValue.Element", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/jdk/java.io/test.ext.yml b/java/ql/test/library-tests/frameworks/jdk/java.io/test.ext.yml
index 35050f48ec0..230733b3ebc 100644
--- a/java/ql/test/library-tests/frameworks/jdk/java.io/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/jdk/java.io/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "getThrowable_messageDefault", "(Object)", "", "Argument[0].SyntheticField[java.lang.Throwable.message]", "ReturnValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/netty/generated/test.ext.yml b/java/ql/test/library-tests/frameworks/netty/generated/test.ext.yml
index f6b69f08632..47a199c75f1 100644
--- a/java/ql/test/library-tests/frameworks/netty/generated/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/netty/generated/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "newWithMapValueDefault", "(Object)", "", "Argument[0]", "ReturnValue.MapValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/frameworks/stream/test.ext.yml b/java/ql/test/library-tests/frameworks/stream/test.ext.yml
index 4f1cc3e38ac..a304f9542a4 100644
--- a/java/ql/test/library-tests/frameworks/stream/test.ext.yml
+++ b/java/ql/test/library-tests/frameworks/stream/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "getElementSpliterator", "(Spliterator)", "", "Argument[0].Element", "ReturnValue", "value", "manual"]
diff --git a/java/ql/test/library-tests/optional/test.ext.yml b/java/ql/test/library-tests/optional/test.ext.yml
index 2aebf3bdb97..24842526782 100644
--- a/java/ql/test/library-tests/optional/test.ext.yml
+++ b/java/ql/test/library-tests/optional/test.ext.yml
@@ -1,6 +1,6 @@
extensions:
- addsTo:
- pack: codeql/java-tests
+ pack: codeql/java-all
extensible: summaryModel
data:
- ["generatedtest", "Test", False, "getStreamElement", "", "", "Argument[0].Element", "ReturnValue", "value", "manual"]
From 53aecb1949dce5c6254bb94c4be6cbac16353d1e Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 30 May 2023 00:17:04 +0000
Subject: [PATCH 172/813] Add changed framework coverage reports
---
csharp/documentation/library-coverage/coverage.csv | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/csharp/documentation/library-coverage/coverage.csv b/csharp/documentation/library-coverage/coverage.csv
index 9c900cf79cd..a4a6a534105 100644
--- a/csharp/documentation/library-coverage/coverage.csv
+++ b/csharp/documentation/library-coverage/coverage.csv
@@ -1,9 +1,9 @@
-package,sink,source,summary,sink:code,sink:encryption-decryptor,sink:encryption-encryptor,sink:encryption-keyprop,sink:encryption-symmetrickey,sink:html,sink:remote,sink:sql,sink:xss,source:file,source:file-write,source:local,source:remote,summary:taint,summary:value
-Dapper,55,,,,,,,,,,55,,,,,,,
+package,sink,source,summary,sink:code-injection,sink:encryption-decryptor,sink:encryption-encryptor,sink:encryption-keyprop,sink:encryption-symmetrickey,sink:file-content-store,sink:html-injection,sink:js-injection,sink:sql-injection,source:file,source:file-write,source:local,source:remote,summary:taint,summary:value
+Dapper,55,,,,,,,,,,,55,,,,,,
JsonToItemsTaskFactory,,,7,,,,,,,,,,,,,,7,
-Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,28,,,,,,,
+Microsoft.ApplicationBlocks.Data,28,,,,,,,,,,,28,,,,,,
Microsoft.CSharp,,,24,,,,,,,,,,,,,,24,
-Microsoft.EntityFrameworkCore,6,,12,,,,,,,,6,,,,,,,12
+Microsoft.EntityFrameworkCore,6,,12,,,,,,,,,6,,,,,,12
Microsoft.Extensions.Caching.Distributed,,,15,,,,,,,,,,,,,,15,
Microsoft.Extensions.Caching.Memory,,,46,,,,,,,,,,,,,,45,1
Microsoft.Extensions.Configuration,,,83,,,,,,,,,,,,,,80,3
@@ -21,8 +21,8 @@ Microsoft.NET.Build.Tasks,,,1,,,,,,,,,,,,,,1,
Microsoft.NETCore.Platforms.BuildTasks,,,4,,,,,,,,,,,,,,4,
Microsoft.VisualBasic,,,10,,,,,,,,,,,,,,5,5
Microsoft.Win32,,,8,,,,,,,,,,,,,,8,
-MySql.Data.MySqlClient,48,,,,,,,,,,48,,,,,,,
+MySql.Data.MySqlClient,48,,,,,,,,,,,48,,,,,,
Newtonsoft.Json,,,91,,,,,,,,,,,,,,73,18
-ServiceStack,194,,7,27,,,,,,75,92,,,,,,7,
-System,65,25,12157,,8,8,9,,4,,33,3,1,17,3,4,10163,1994
+ServiceStack,194,,7,27,,,,,75,,,92,,,,,7,
+System,65,25,12157,,8,8,9,,,4,3,33,1,17,3,4,10163,1994
Windows.Security.Cryptography.Core,1,,,,,,,1,,,,,,,,,,
From 39a07d42a1bca7edaffc56b4ec5beb6c621da668 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 30 May 2023 04:03:50 +0000
Subject: [PATCH 173/813] Bump chrono from 0.4.24 to 0.4.25 in /ql
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.24 to 0.4.25.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.24...v0.4.25)
---
updated-dependencies:
- dependency-name: chrono
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
ql/Cargo.lock | Bin 31708 -> 31667 bytes
ql/buramu/Cargo.toml | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/ql/Cargo.lock b/ql/Cargo.lock
index 60a0ad5919a43dba84fa52a6aa9930bd588cf198..76437a85e057d43e2bf6bd6efa08833e2d6e19f3 100644
GIT binary patch
delta 197
zcmWlTJ8l9o6aWQ*0)-YKktRV(gZS8f#&4}OThPN5{C+2(;u<~xJr!sVlO~r)MVrlN
z`k9~SpnDGP%i(BqzDV62X7#u^wwimQrvTBwgD2;RJ#Yx(k~X|I!;r*N0^bxyrGP1C
zB%)|)u70fM-P>?6slV^1)`S>PdP~+lu{IvTp!5OslF5J*Mo}dytcArXNg+{8YUH3k
cUgq_>9#8%s%egGq_51a+n@;1l3tzYP4_iJx6951J
delta 203
zcmXxcF-k*05CBjTkf5YXktU%0UimxoXLg66#2ZL%u)DJpQW!AA-YbN?M^N?}9>D_$
z9w4nYmalp3Jo-A1-n()89?vf8+v4eAD40o{^vqEWK+U6PDAZ@OtOcW|U=YI)l5?0S
zusRFuAtd(o_&Bd^+D*2*L)nyl`<&k2{iFV@(+YqTF^DCkULp)x&?tkH#7PVMeMy3f
m^^O@?%_ykPY@S%MO8r<~U)^-O*XP^AK5pwvw`dp3)9M$zN
Date: Tue, 30 May 2023 10:25:36 +0200
Subject: [PATCH 174/813] add support for sanitizers
---
.../AutomodelApplicationModeCharacteristics.qll | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 785b56d31da..4e753a4479d 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -15,6 +15,7 @@ private import semmle.code.java.security.QueryInjection
private import semmle.code.java.security.RequestForgery
private import semmle.code.java.dataflow.internal.ModelExclusions as ModelExclusions
private import AutomodelSharedUtil as AutomodelSharedUtil
+private import semmle.code.java.security.PathSanitizer as PathSanitizer
import AutomodelSharedCharacteristics as SharedCharacteristics
import AutomodelEndpointTypes as AutomodelEndpointTypes
@@ -48,7 +49,19 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
class RelatedLocationType = JavaRelatedLocationType;
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
- predicate isSanitizer(Endpoint e, EndpointType t) { none() }
+ predicate isSanitizer(Endpoint e, EndpointType t) {
+ (
+ exists(t) and
+ e.getType() instanceof BoxedType
+ or
+ e.getType() instanceof PrimitiveType
+ or
+ e.getType() instanceof NumberType
+ )
+ or
+ t instanceof AutomodelEndpointTypes::TaintedPathSinkType and
+ e instanceof PathSanitizer::PathInjectionSanitizer
+ }
RelatedLocation asLocation(Endpoint e) { result = e.asExpr() }
From d50617202746b8cbb8558f8486975a804e9a71fd Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 30 May 2023 09:11:00 +0100
Subject: [PATCH 175/813] Swift: Change note.
---
swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md
diff --git a/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md b/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md
new file mode 100644
index 00000000000..03de16f4269
--- /dev/null
+++ b/swift/ql/lib/change-notes/2023-05-30-shared-sensitive.md
@@ -0,0 +1,4 @@
+---
+category: majorAnalysis
+---
+* Incorporated the cross-language `SensitiveDataHeuristics.qll` heuristics library into the Swift `SensitiveExprs.qll` library. This adds a number of new heuristics enhancing detection from the library.
\ No newline at end of file
From 138bfad3d06c7ba84690f0e291721b91c9bfc174 Mon Sep 17 00:00:00 2001
From: Tamas Vajk
Date: Tue, 30 May 2023 12:00:31 +0200
Subject: [PATCH 176/813] Add change note
---
csharp/ql/lib/change-notes/2023-05-30-source-generators.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 csharp/ql/lib/change-notes/2023-05-30-source-generators.md
diff --git a/csharp/ql/lib/change-notes/2023-05-30-source-generators.md b/csharp/ql/lib/change-notes/2023-05-30-source-generators.md
new file mode 100644
index 00000000000..5483ce6af35
--- /dev/null
+++ b/csharp/ql/lib/change-notes/2023-05-30-source-generators.md
@@ -0,0 +1,4 @@
+---
+category: majorAnalysis
+---
+* The extractor has been changed to run after the traced compiler call. This allows inspecting compiler generated files, such as the output of source generators. With this change, `.cshtml` files and their generated `.cshtml.g.cs` counterparts are extracted on dotnet 6 and above.
From 47b2d48da25ed1cd49c4e3dd5647b362477f2682 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Wed, 17 May 2023 23:16:59 +0200
Subject: [PATCH 177/813] python: add tests - add `getACallSimple` to
`SummarizedCallable` (by adding it to `LibraryCallable`)
---
.../new/internal/DataFlowDispatch.qll | 3 +
.../typetracking-summaries/TestSummaries.qll | 189 ++++++++++++++++++
.../typetracking-summaries/summaries.py | 60 ++++++
.../typetracking-summaries/tracked.expected | 0
.../typetracking-summaries/tracked.ql | 36 ++++
5 files changed, 288 insertions(+)
create mode 100644 python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll
create mode 100644 python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
create mode 100644 python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected
create mode 100644 python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll
index cdca96cc4ac..8dad2e8a032 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/DataFlowDispatch.qll
@@ -250,6 +250,9 @@ abstract class LibraryCallable extends string {
/** Gets a call to this library callable. */
abstract CallCfgNode getACall();
+ /** Same as `getACall` but without referring to the call graph or API graph. */
+ CallCfgNode getACallSimple() { none() }
+
/** Gets a data-flow node, where this library callable is used as a call-back. */
abstract ArgumentNode getACallback();
}
diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll b/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll
new file mode 100644
index 00000000000..1a6a504e1ee
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/typetracking-summaries/TestSummaries.qll
@@ -0,0 +1,189 @@
+private import python
+private import semmle.python.dataflow.new.FlowSummary
+private import semmle.python.ApiGraphs
+
+/**
+ * This module ensures that the `callStep` predicate in
+ * our type tracker implelemtation does not refer to the
+ * `getACall` predicate on `SummarizedCallable`.
+ */
+module RecursionGuard {
+ private import semmle.python.dataflow.new.internal.TypeTrackerSpecific as TT
+
+ private class RecursionGuard extends SummarizedCallable {
+ RecursionGuard() { this = "RecursionGuard" }
+
+ override DataFlow::CallCfgNode getACall() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this and
+ (TT::callStep(_, _) implies any())
+ }
+
+ override DataFlow::CallCfgNode getACallSimple() { none() }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+ }
+
+ predicate test(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
+ TT::levelStepNoCall(nodeFrom, nodeTo)
+ }
+}
+
+private class SummarizedCallableIdentity extends SummarizedCallable {
+ SummarizedCallableIdentity() { this = "identity" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[0]" and
+ output = "ReturnValue" and
+ preservesValue = true
+ }
+}
+
+// For lambda flow to work, implement lambdaCall and lambdaCreation
+private class SummarizedCallableApplyLambda extends SummarizedCallable {
+ SummarizedCallableApplyLambda() { this = "apply_lambda" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[1]" and
+ output = "Argument[0].Parameter[0]" and
+ preservesValue = true
+ or
+ input = "Argument[0].ReturnValue" and
+ output = "ReturnValue" and
+ preservesValue = true
+ }
+}
+
+private class SummarizedCallableReversed extends SummarizedCallable {
+ SummarizedCallableReversed() { this = "reversed" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[0].ListElement" and
+ output = "ReturnValue.ListElement" and
+ preservesValue = true
+ }
+}
+
+private class SummarizedCallableMap extends SummarizedCallable {
+ SummarizedCallableMap() { this = "list_map" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[1].ListElement" and
+ output = "Argument[0].Parameter[0]" and
+ preservesValue = true
+ or
+ input = "Argument[0].ReturnValue" and
+ output = "ReturnValue.ListElement" and
+ preservesValue = true
+ }
+}
+
+private class SummarizedCallableAppend extends SummarizedCallable {
+ SummarizedCallableAppend() { this = "append_to_list" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[0]" and
+ output = "ReturnValue" and
+ preservesValue = false
+ or
+ input = "Argument[1]" and
+ output = "ReturnValue.ListElement" and
+ preservesValue = true
+ }
+}
+
+private class SummarizedCallableJsonLoads extends SummarizedCallable {
+ SummarizedCallableJsonLoads() { this = "json.loads" }
+
+ override DataFlow::CallCfgNode getACall() {
+ result = API::moduleImport("json").getMember("loads").getACall()
+ }
+
+ override DataFlow::CallCfgNode getACallSimple() { none() }
+
+ override DataFlow::ArgumentNode getACallback() {
+ result = API::moduleImport("json").getMember("loads").getAValueReachableFromSource()
+ }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[0]" and
+ output = "ReturnValue.ListElement" and
+ preservesValue = true
+ }
+}
+
+// read and store
+private class SummarizedCallableReadSecret extends SummarizedCallable {
+ SummarizedCallableReadSecret() { this = "read_secret" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[0].Attribute[secret]" and
+ output = "ReturnValue" and
+ preservesValue = true
+ }
+}
+
+private class SummarizedCallableSetSecret extends SummarizedCallable {
+ SummarizedCallableSetSecret() { this = "set_secret" }
+
+ override DataFlow::CallCfgNode getACall() { none() }
+
+ override DataFlow::CallCfgNode getACallSimple() {
+ result.getFunction().asCfgNode().(NameNode).getId() = this
+ }
+
+ override DataFlow::ArgumentNode getACallback() { result.asExpr().(Name).getId() = this }
+
+ override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
+ input = "Argument[1]" and
+ output = "Argument[0].Attribute[secret]" and
+ preservesValue = true
+ }
+}
diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
new file mode 100644
index 00000000000..f7affa6036f
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
@@ -0,0 +1,60 @@
+import sys
+import os
+
+# Simple summary
+tainted = identity(tracked) # $ tracked
+tainted # $ MISSING: tracked
+
+# Lambda summary
+# I think the missing result is expected because type tracking
+# is not allowed to flow back out of a call.
+tainted_lambda = apply_lambda(lambda x: x, tracked) # $ tracked
+tainted_lambda # $ MISSING: tracked
+
+# A lambda that directly introduces taint
+bad_lambda = apply_lambda(lambda x: tracked, 1) # $ tracked
+bad_lambda # $ MISSING: tracked
+
+# A lambda that breaks the flow
+untainted_lambda = apply_lambda(lambda x: 1, tracked) # $ tracked
+untainted_lambda
+
+# Collection summaries
+tainted_list = reversed([tracked]) # $ tracked
+tl = tainted_list[0]
+tl # $ MISSING: tracked
+
+# Complex summaries
+def add_colon(x):
+ return x + ":"
+
+tainted_mapped = list_map(add_colon, [tracked]) # $ tracked
+tm = tainted_mapped[0]
+tm # $ MISSING: tracked
+
+def explicit_identity(x):
+ return x
+
+tainted_mapped_explicit = list_map(explicit_identity, [tracked]) # $ tracked
+tainted_mapped_explicit[0] # $ MISSING: tracked
+
+tainted_mapped_summary = list_map(identity, [tracked]) # $ tracked
+tms = tainted_mapped_summary[0]
+tms # $ MISSING: tracked
+
+another_tainted_list = append_to_list([], tracked) # $ tracked
+atl = another_tainted_list[0]
+atl # $ MISSING: tracked
+
+from json import loads as json_loads
+tainted_resultlist = json_loads(tracked) # $ tracked
+tr = tainted_resultlist[0]
+tr # $ MISSING: tracked
+
+x.secret = tracked # $ tracked=secret tracked
+r = read_secret(x) # $ tracked=secret MISSING: tracked
+r # $ MISSING: tracked
+
+y # $ MISSING: tracked=secret
+set_secret(y, tracked) # $ tracked MISSING: tracked=secret
+y.secret # $ MISSING: tracked tracked=secret
\ No newline at end of file
diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql
new file mode 100644
index 00000000000..e5bf62053a0
--- /dev/null
+++ b/python/ql/test/experimental/dataflow/typetracking-summaries/tracked.ql
@@ -0,0 +1,36 @@
+import python
+import semmle.python.dataflow.new.DataFlow
+import semmle.python.dataflow.new.TypeTracker
+import TestUtilities.InlineExpectationsTest
+import semmle.python.ApiGraphs
+import TestSummaries
+
+// -----------------------------------------------------------------------------
+// tracked
+// -----------------------------------------------------------------------------
+private DataFlow::TypeTrackingNode tracked(TypeTracker t) {
+ t.start() and
+ result.asCfgNode() = any(NameNode n | n.getId() = "tracked")
+ or
+ exists(TypeTracker t2 | result = tracked(t2).track(t2, t))
+}
+
+class TrackedTest extends InlineExpectationsTest {
+ TrackedTest() { this = "TrackedTest" }
+
+ override string getARelevantTag() { result = "tracked" }
+
+ override predicate hasActualResult(Location location, string element, string tag, string value) {
+ exists(DataFlow::Node e, TypeTracker t |
+ exists(e.getLocation().getFile().getRelativePath()) and
+ e.getLocation().getStartLine() > 0 and
+ tracked(t).flowsTo(e) and
+ // Module variables have no sensible location, and hence can't be annotated.
+ not e instanceof DataFlow::ModuleVariableNode and
+ tag = "tracked" and
+ location = e.getLocation() and
+ value = t.getAttr() and
+ element = e.toString()
+ )
+ }
+}
From 73aa790cdd37f09cec4ae97c5efed4d1a5cfc02a Mon Sep 17 00:00:00 2001
From: Taus
Date: Tue, 30 May 2023 11:22:26 +0000
Subject: [PATCH 178/813] Java: Improve sampling strategy
Instead of the "random" sampling used before (which could -- in rare circumstances -- end up sampling fewer points than we want) we now sample an equally distributed set of points.
---
.../AutomodelApplicationModeExtractNegativeExamples.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
index 0fe785de26b..d817d3a244a 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
@@ -18,7 +18,7 @@ private import AutomodelSharedUtil
*/
bindingset[limit]
Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
- exists(int n |
+ exists(int n, int num_endpoints | num_endpoints = count(Endpoint e | c.appliesToEndpoint(e)) |
result =
rank[n](Endpoint e, Location loc |
loc = e.getLocation() and c.appliesToEndpoint(e)
@@ -29,7 +29,7 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
loc.getEndLine(), loc.getEndColumn()
) and
// we order the endpoints by location, but (to avoid bias) we select the indices semi-randomly
- n = 1 + (([1 .. limit] * 271) % count(Endpoint e | c.appliesToEndpoint(e)))
+ n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
)
}
From 2daa9577bbbcdf762fb00c96dcd20849245e75b0 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 16 May 2023 14:20:29 +0200
Subject: [PATCH 179/813] ruby/python: implement shared module ruby: - create
new shared file `SummaryTypeTracker.qll` - move much logic into the module -
instantiate the module - remove old logic, now provided by module
python:
- clone shared file
- instantiate module
- use (some of the) steps provided by the module
---
config/identical-files.json | 4 +
.../new/internal/SummaryTypeTracker.qll | 382 ++++++++++++++++++
.../new/internal/TypeTrackerSpecific.qll | 128 +++++-
.../typetracking-summaries/summaries.py | 14 +-
.../ruby/typetracking/SummaryTypeTracker.qll | 382 ++++++++++++++++++
.../ruby/typetracking/TypeTrackerSpecific.qll | 370 +++++------------
6 files changed, 1011 insertions(+), 269 deletions(-)
create mode 100644 python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll
create mode 100644 ruby/ql/lib/codeql/ruby/typetracking/SummaryTypeTracker.qll
diff --git a/config/identical-files.json b/config/identical-files.json
index 29fae2d3855..3d84e84dc9c 100644
--- a/config/identical-files.json
+++ b/config/identical-files.json
@@ -522,6 +522,10 @@
"python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll",
"ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll"
],
+ "SummaryTypeTracker": [
+ "python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll",
+ "ruby/ql/lib/codeql/ruby/typetracking/SummaryTypeTracker.qll"
+ ],
"AccessPathSyntax": [
"csharp/ql/lib/semmle/code/csharp/dataflow/internal/AccessPathSyntax.qll",
"go/ql/lib/semmle/go/dataflow/internal/AccessPathSyntax.qll",
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll
new file mode 100644
index 00000000000..310dec3aadf
--- /dev/null
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/SummaryTypeTracker.qll
@@ -0,0 +1,382 @@
+/**
+ * Provides the implementation of a summary type tracker, that is type tracking through flow summaries.
+ * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output`
+ * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`.
+ */
+
+/** The classes and predicates needed to generate a summary type tracker. */
+signature module Input {
+ // Dataflow nodes
+ class Node;
+
+ // Content
+ class TypeTrackerContent;
+
+ class TypeTrackerContentFilter;
+
+ // Relating content and filters
+ /**
+ * Gets a content filter to use for a `WithoutContent[content]` step, or has no result if
+ * the step should be treated as ordinary flow.
+ *
+ * `WithoutContent` is often used to perform strong updates on individual collection elements, but for
+ * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful
+ * for restricting the type of an object, and in these cases we translate it to a filter.
+ */
+ TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content);
+
+ /**
+ * Gets a content filter to use for a `WithContent[content]` step, or has no result if
+ * the step cannot be handled by type-tracking.
+ *
+ * `WithContent` is often used to perform strong updates on individual collection elements (or rather
+ * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive.
+ * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter.
+ */
+ TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content);
+
+ // Summaries and their stacks
+ class SummaryComponent;
+
+ class SummaryComponentStack {
+ SummaryComponent head();
+ }
+
+ /** Gets a singleton stack containing `component`. */
+ SummaryComponentStack singleton(SummaryComponent component);
+
+ /**
+ * Gets the stack obtained by pushing `head` onto `tail`.
+ */
+ SummaryComponentStack push(SummaryComponent component, SummaryComponentStack stack);
+
+ /** Gets a singleton stack representing a return. */
+ SummaryComponent return();
+
+ // Relating content to summaries
+ /** Gets a summary component for content `c`. */
+ SummaryComponent content(TypeTrackerContent contents);
+
+ /** Gets a summary component where data is not allowed to be stored in `c`. */
+ SummaryComponent withoutContent(TypeTrackerContent contents);
+
+ /** Gets a summary component where data must be stored in `c`. */
+ SummaryComponent withContent(TypeTrackerContent contents);
+
+ // Callables
+ class SummarizedCallable {
+ predicate propagatesFlow(
+ SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
+ );
+ }
+
+ // Relating nodes to summaries
+ /** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */
+ Node argumentOf(Node call, SummaryComponent arg);
+
+ /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
+ Node parameterOf(Node callable, SummaryComponent param);
+
+ /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */
+ Node returnOf(Node callable, SummaryComponent return);
+
+ // Specific summary handling
+ /** Holds if component should be treated as a level step by type tracking. */
+ predicate componentLevelStep(SummaryComponent component);
+
+ /** Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`. */
+ predicate isNonLocal(SummaryComponent component);
+
+ // Relating callables to nodes
+ /** Gets a dataflow node respresenting a call to `callable`. */
+ Node callTo(SummarizedCallable callable);
+}
+
+/**
+ * The predicates provided by a summary type tracker.
+ * These are meant to be used in `TypeTrackerSpecific.qll`
+ * inside the predicates of the same names.
+ */
+signature module Output {
+ /**
+ * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph.
+ */
+ predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo);
+
+ /**
+ * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
+ */
+ predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
+
+ /**
+ * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
+ */
+ predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
+
+ /**
+ * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
+ */
+ predicate basicLoadStoreStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
+ I::TypeTrackerContent storeContent
+ );
+
+ /**
+ * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
+ */
+ predicate basicWithoutContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ );
+
+ /**
+ * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
+ */
+ predicate basicWithContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ );
+}
+
+/**
+ * Implementation of the summary type tracker, that is type tracking through flow summaries.
+ */
+module SummaryFlow implements Output {
+ pragma[nomagic]
+ private predicate hasLoadSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ callable.propagatesFlow(I::push(I::content(contents), input), output, true) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head())
+ }
+
+ pragma[nomagic]
+ private predicate hasStoreSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ (
+ callable.propagatesFlow(input, I::push(I::content(contents), output), true)
+ or
+ // Allow the input to start with an arbitrary WithoutContent[X].
+ // Since type-tracking only tracks one content deep, and we're about to store into another content,
+ // we're already preventing the input from being in a content.
+ callable
+ .propagatesFlow(I::push(I::withoutContent(_), input),
+ I::push(I::content(contents), output), true)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasLoadStoreSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent loadContents,
+ I::TypeTrackerContent storeContents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ callable
+ .propagatesFlow(I::push(I::content(loadContents), input),
+ I::push(I::content(storeContents), output), true) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head())
+ }
+
+ pragma[nomagic]
+ private predicate hasWithoutContentSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
+ I::SummaryComponentStack input, I::SummaryComponentStack output
+ ) {
+ exists(I::TypeTrackerContent content |
+ callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and
+ filter = I::getFilterFromWithoutContentStep(content) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ input != output
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasWithContentSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
+ I::SummaryComponentStack input, I::SummaryComponentStack output
+ ) {
+ exists(I::TypeTrackerContent content |
+ callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and
+ filter = I::getFilterFromWithContentStep(content) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ input != output
+ )
+ }
+
+ /**
+ * Gets a data flow I::Node corresponding an argument or return value of `call`,
+ * as specified by `component`.
+ */
+ bindingset[call, component]
+ private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) {
+ result = I::argumentOf(call, component)
+ or
+ component = I::return() and
+ result = call
+ }
+
+ /**
+ * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to
+ * be evaluated locally at its call sites.
+ */
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStack(
+ I::SummarizedCallable callable, I::SummaryComponentStack stack
+ ) {
+ exists(I::callTo(callable)) and
+ (
+ callable.propagatesFlow(stack, _, true)
+ or
+ callable.propagatesFlow(_, stack, true)
+ or
+ // include store summaries as they may skip an initial step at the input
+ hasStoreSummary(callable, _, stack, _)
+ )
+ or
+ dependsOnSummaryComponentStackCons(callable, _, stack)
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackCons(
+ I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
+ ) {
+ dependsOnSummaryComponentStack(callable, I::push(head, tail))
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackConsLocal(
+ I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
+ ) {
+ dependsOnSummaryComponentStackCons(callable, head, tail) and
+ not I::isNonLocal(head)
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackLeaf(
+ I::SummarizedCallable callable, I::SummaryComponent leaf
+ ) {
+ dependsOnSummaryComponentStack(callable, I::singleton(leaf))
+ }
+
+ /**
+ * Gets a data flow I::Node corresponding to the local input or output of `call`
+ * identified by `stack`, if possible.
+ */
+ pragma[nomagic]
+ private I::Node evaluateSummaryComponentStackLocal(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack
+ ) {
+ exists(I::SummaryComponent component |
+ dependsOnSummaryComponentStackLeaf(callable, component) and
+ stack = I::singleton(component) and
+ call = I::callTo(callable) and
+ result = evaluateSummaryComponentLocal(call, component)
+ )
+ or
+ exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail |
+ prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
+ dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
+ pragma[only_bind_out](tail)) and
+ stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
+ |
+ result = I::parameterOf(prev, head)
+ or
+ result = I::returnOf(prev, head)
+ or
+ I::componentLevelStep(head) and
+ result = prev
+ )
+ }
+
+ // Implement Output
+ predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ callable.propagatesFlow(input, output, true) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasLoadSummary(callable, content, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasStoreSummary(callable, content, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicLoadStoreStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
+ I::TypeTrackerContent storeContent
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicWithoutContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicWithContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+}
diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll
index 9e05b7869c5..81e673dc30b 100644
--- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll
+++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTrackerSpecific.qll
@@ -61,7 +61,9 @@ predicate capturedJumpStep(Node nodeFrom, Node nodeTo) {
predicate levelStepCall(Node nodeFrom, Node nodeTo) { none() }
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
-predicate levelStepNoCall(Node nodeFrom, Node nodeTo) { none() }
+predicate levelStepNoCall(Node nodeFrom, Node nodeTo) {
+ TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
+}
/**
* Gets the name of a possible piece of content. For Python, this is currently only attribute names,
@@ -108,6 +110,12 @@ predicate basicStoreStep(Node nodeFrom, Node nodeTo, string content) {
nodeFrom = a.getValue() and
nodeTo = a.getObject()
)
+ or
+ exists(DataFlowPublic::ContentSet contents |
+ contents.(DataFlowPublic::AttributeContent).getAttribute() = content
+ |
+ TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
+ )
}
/**
@@ -119,13 +127,24 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, string content) {
nodeFrom = a.getObject() and
nodeTo = a
)
+ or
+ exists(DataFlowPublic::ContentSet contents |
+ contents.(DataFlowPublic::AttributeContent).getAttribute() = content
+ |
+ TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
+ )
}
/**
* Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
*/
predicate basicLoadStoreStep(Node nodeFrom, Node nodeTo, string loadContent, string storeContent) {
- none()
+ exists(DataFlowPublic::ContentSet loadContents, DataFlowPublic::ContentSet storeContents |
+ loadContents.(DataFlowPublic::AttributeContent).getAttribute() = loadContent and
+ storeContents.(DataFlowPublic::AttributeContent).getAttribute() = storeContent
+ |
+ TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContents, storeContents)
+ )
}
/**
@@ -144,3 +163,108 @@ predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter)
class Boolean extends boolean {
Boolean() { this = true or this = false }
}
+
+private import SummaryTypeTracker as SummaryTypeTracker
+private import semmle.python.dataflow.new.FlowSummary as FlowSummary
+private import semmle.python.dataflow.new.internal.DataFlowDispatch as DataFlowDispatch
+
+pragma[noinline]
+private predicate argumentPositionMatch(
+ DataFlowPublic::CallCfgNode call, DataFlowPublic::ArgumentNode arg,
+ DataFlowDispatch::ParameterPosition ppos
+) {
+ exists(DataFlowDispatch::ArgumentPosition apos, DataFlowPrivate::DataFlowCall c |
+ c.getNode() = call.asCfgNode() and
+ arg.argumentOf(c, apos) and
+ DataFlowDispatch::parameterMatch(ppos, apos)
+ )
+}
+
+module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
+ // Dataflow nodes
+ class Node = DataFlowPublic::Node;
+
+ // Content
+ class TypeTrackerContent = DataFlowPublic::ContentSet;
+
+ class TypeTrackerContentFilter = ContentFilter;
+
+ TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) { none() }
+
+ TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) { none() }
+
+ // Callables
+ class SummarizedCallable = FlowSummary::SummarizedCallable;
+
+ // Summaries and their stacks
+ class SummaryComponent = FlowSummary::SummaryComponent;
+
+ class SummaryComponentStack = FlowSummary::SummaryComponentStack;
+
+ SummaryComponentStack singleton(SummaryComponent component) {
+ result = FlowSummary::SummaryComponentStack::singleton(component)
+ }
+
+ SummaryComponentStack push(SummaryComponent component, SummaryComponentStack stack) {
+ result = FlowSummary::SummaryComponentStack::push(component, stack)
+ }
+
+ // Relating content to summaries
+ SummaryComponent content(TypeTrackerContent contents) {
+ result = FlowSummary::SummaryComponent::content(contents)
+ }
+
+ SummaryComponent withoutContent(TypeTrackerContent contents) { none() }
+
+ SummaryComponent withContent(TypeTrackerContent contents) { none() }
+
+ SummaryComponent return() { result = FlowSummary::SummaryComponent::return() }
+
+ // Relating nodes to summaries
+ Node argumentOf(Node call, SummaryComponent arg) {
+ exists(DataFlowDispatch::ParameterPosition pos |
+ arg = FlowSummary::SummaryComponent::argument(pos) and
+ argumentPositionMatch(call, result, pos)
+ )
+ }
+
+ Node parameterOf(Node callable, SummaryComponent param) {
+ exists(
+ DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos, Parameter p
+ |
+ param = FlowSummary::SummaryComponent::parameter(apos) and
+ DataFlowDispatch::parameterMatch(ppos, apos) and
+ // pick the SsaNode rather than the CfgNode
+ result.asVar().getDefinition().(ParameterDefinition).getParameter() = p and
+ (
+ exists(int i | ppos.isPositional(i) |
+ p = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getArg(i)
+ )
+ or
+ exists(string name | ppos.isKeyword(name) |
+ p = callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getArgByName(name)
+ )
+ )
+ )
+ }
+
+ Node returnOf(Node callable, SummaryComponent return) {
+ return = FlowSummary::SummaryComponent::return() and
+ // result should be return value of callable which should be a lambda
+ result.asCfgNode() =
+ callable.getALocalSource().asExpr().(CallableExpr).getInnerScope().getAReturnValueFlowNode()
+ }
+
+ // Specific summary handling
+ predicate componentLevelStep(SummaryComponent component) { none() }
+
+ pragma[nomagic]
+ predicate isNonLocal(SummaryComponent component) {
+ component = FlowSummary::SummaryComponent::content(_)
+ }
+
+ // Relating callables to nodes
+ Node callTo(SummarizedCallable callable) { result = callable.getACallSimple() }
+}
+
+module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow;
diff --git a/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
index f7affa6036f..728456cc711 100644
--- a/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
+++ b/python/ql/test/experimental/dataflow/typetracking-summaries/summaries.py
@@ -3,7 +3,7 @@ import os
# Simple summary
tainted = identity(tracked) # $ tracked
-tainted # $ MISSING: tracked
+tainted # $ tracked
# Lambda summary
# I think the missing result is expected because type tracking
@@ -13,7 +13,7 @@ tainted_lambda # $ MISSING: tracked
# A lambda that directly introduces taint
bad_lambda = apply_lambda(lambda x: tracked, 1) # $ tracked
-bad_lambda # $ MISSING: tracked
+bad_lambda # $ tracked
# A lambda that breaks the flow
untainted_lambda = apply_lambda(lambda x: 1, tracked) # $ tracked
@@ -52,9 +52,9 @@ tr = tainted_resultlist[0]
tr # $ MISSING: tracked
x.secret = tracked # $ tracked=secret tracked
-r = read_secret(x) # $ tracked=secret MISSING: tracked
-r # $ MISSING: tracked
+r = read_secret(x) # $ tracked=secret tracked
+r # $ tracked
-y # $ MISSING: tracked=secret
-set_secret(y, tracked) # $ tracked MISSING: tracked=secret
-y.secret # $ MISSING: tracked tracked=secret
\ No newline at end of file
+y # $ tracked=secret
+set_secret(y, tracked) # $ tracked tracked=secret
+y.secret # $ tracked tracked=secret
\ No newline at end of file
diff --git a/ruby/ql/lib/codeql/ruby/typetracking/SummaryTypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/SummaryTypeTracker.qll
new file mode 100644
index 00000000000..310dec3aadf
--- /dev/null
+++ b/ruby/ql/lib/codeql/ruby/typetracking/SummaryTypeTracker.qll
@@ -0,0 +1,382 @@
+/**
+ * Provides the implementation of a summary type tracker, that is type tracking through flow summaries.
+ * To use this, you must implement the `Input` signature. You can then use the predicates in the `Output`
+ * signature to implement the predicates of the same names inside `TypeTrackerSpecific.qll`.
+ */
+
+/** The classes and predicates needed to generate a summary type tracker. */
+signature module Input {
+ // Dataflow nodes
+ class Node;
+
+ // Content
+ class TypeTrackerContent;
+
+ class TypeTrackerContentFilter;
+
+ // Relating content and filters
+ /**
+ * Gets a content filter to use for a `WithoutContent[content]` step, or has no result if
+ * the step should be treated as ordinary flow.
+ *
+ * `WithoutContent` is often used to perform strong updates on individual collection elements, but for
+ * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful
+ * for restricting the type of an object, and in these cases we translate it to a filter.
+ */
+ TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content);
+
+ /**
+ * Gets a content filter to use for a `WithContent[content]` step, or has no result if
+ * the step cannot be handled by type-tracking.
+ *
+ * `WithContent` is often used to perform strong updates on individual collection elements (or rather
+ * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive.
+ * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter.
+ */
+ TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content);
+
+ // Summaries and their stacks
+ class SummaryComponent;
+
+ class SummaryComponentStack {
+ SummaryComponent head();
+ }
+
+ /** Gets a singleton stack containing `component`. */
+ SummaryComponentStack singleton(SummaryComponent component);
+
+ /**
+ * Gets the stack obtained by pushing `head` onto `tail`.
+ */
+ SummaryComponentStack push(SummaryComponent component, SummaryComponentStack stack);
+
+ /** Gets a singleton stack representing a return. */
+ SummaryComponent return();
+
+ // Relating content to summaries
+ /** Gets a summary component for content `c`. */
+ SummaryComponent content(TypeTrackerContent contents);
+
+ /** Gets a summary component where data is not allowed to be stored in `c`. */
+ SummaryComponent withoutContent(TypeTrackerContent contents);
+
+ /** Gets a summary component where data must be stored in `c`. */
+ SummaryComponent withContent(TypeTrackerContent contents);
+
+ // Callables
+ class SummarizedCallable {
+ predicate propagatesFlow(
+ SummaryComponentStack input, SummaryComponentStack output, boolean preservesValue
+ );
+ }
+
+ // Relating nodes to summaries
+ /** Gets a dataflow node respresenting the argument of `call` indicated by `arg`. */
+ Node argumentOf(Node call, SummaryComponent arg);
+
+ /** Gets a dataflow node respresenting the parameter of `callable` indicated by `param`. */
+ Node parameterOf(Node callable, SummaryComponent param);
+
+ /** Gets a dataflow node respresenting the return of `callable` indicated by `return`. */
+ Node returnOf(Node callable, SummaryComponent return);
+
+ // Specific summary handling
+ /** Holds if component should be treated as a level step by type tracking. */
+ predicate componentLevelStep(SummaryComponent component);
+
+ /** Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`. */
+ predicate isNonLocal(SummaryComponent component);
+
+ // Relating callables to nodes
+ /** Gets a dataflow node respresenting a call to `callable`. */
+ Node callTo(SummarizedCallable callable);
+}
+
+/**
+ * The predicates provided by a summary type tracker.
+ * These are meant to be used in `TypeTrackerSpecific.qll`
+ * inside the predicates of the same names.
+ */
+signature module Output {
+ /**
+ * Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph.
+ */
+ predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo);
+
+ /**
+ * Holds if `nodeTo` is the result of accessing the `content` content of `nodeFrom`.
+ */
+ predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
+
+ /**
+ * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
+ */
+ predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content);
+
+ /**
+ * Holds if the `loadContent` of `nodeFrom` is stored in the `storeContent` of `nodeTo`.
+ */
+ predicate basicLoadStoreStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
+ I::TypeTrackerContent storeContent
+ );
+
+ /**
+ * Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
+ */
+ predicate basicWithoutContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ );
+
+ /**
+ * Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
+ */
+ predicate basicWithContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ );
+}
+
+/**
+ * Implementation of the summary type tracker, that is type tracking through flow summaries.
+ */
+module SummaryFlow implements Output {
+ pragma[nomagic]
+ private predicate hasLoadSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ callable.propagatesFlow(I::push(I::content(contents), input), output, true) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head())
+ }
+
+ pragma[nomagic]
+ private predicate hasStoreSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent contents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ (
+ callable.propagatesFlow(input, I::push(I::content(contents), output), true)
+ or
+ // Allow the input to start with an arbitrary WithoutContent[X].
+ // Since type-tracking only tracks one content deep, and we're about to store into another content,
+ // we're already preventing the input from being in a content.
+ callable
+ .propagatesFlow(I::push(I::withoutContent(_), input),
+ I::push(I::content(contents), output), true)
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasLoadStoreSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContent loadContents,
+ I::TypeTrackerContent storeContents, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ ) {
+ callable
+ .propagatesFlow(I::push(I::content(loadContents), input),
+ I::push(I::content(storeContents), output), true) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head())
+ }
+
+ pragma[nomagic]
+ private predicate hasWithoutContentSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
+ I::SummaryComponentStack input, I::SummaryComponentStack output
+ ) {
+ exists(I::TypeTrackerContent content |
+ callable.propagatesFlow(I::push(I::withoutContent(content), input), output, true) and
+ filter = I::getFilterFromWithoutContentStep(content) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ input != output
+ )
+ }
+
+ pragma[nomagic]
+ private predicate hasWithContentSummary(
+ I::SummarizedCallable callable, I::TypeTrackerContentFilter filter,
+ I::SummaryComponentStack input, I::SummaryComponentStack output
+ ) {
+ exists(I::TypeTrackerContent content |
+ callable.propagatesFlow(I::push(I::withContent(content), input), output, true) and
+ filter = I::getFilterFromWithContentStep(content) and
+ not I::isNonLocal(input.head()) and
+ not I::isNonLocal(output.head()) and
+ input != output
+ )
+ }
+
+ /**
+ * Gets a data flow I::Node corresponding an argument or return value of `call`,
+ * as specified by `component`.
+ */
+ bindingset[call, component]
+ private I::Node evaluateSummaryComponentLocal(I::Node call, I::SummaryComponent component) {
+ result = I::argumentOf(call, component)
+ or
+ component = I::return() and
+ result = call
+ }
+
+ /**
+ * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to
+ * be evaluated locally at its call sites.
+ */
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStack(
+ I::SummarizedCallable callable, I::SummaryComponentStack stack
+ ) {
+ exists(I::callTo(callable)) and
+ (
+ callable.propagatesFlow(stack, _, true)
+ or
+ callable.propagatesFlow(_, stack, true)
+ or
+ // include store summaries as they may skip an initial step at the input
+ hasStoreSummary(callable, _, stack, _)
+ )
+ or
+ dependsOnSummaryComponentStackCons(callable, _, stack)
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackCons(
+ I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
+ ) {
+ dependsOnSummaryComponentStack(callable, I::push(head, tail))
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackConsLocal(
+ I::SummarizedCallable callable, I::SummaryComponent head, I::SummaryComponentStack tail
+ ) {
+ dependsOnSummaryComponentStackCons(callable, head, tail) and
+ not I::isNonLocal(head)
+ }
+
+ pragma[nomagic]
+ private predicate dependsOnSummaryComponentStackLeaf(
+ I::SummarizedCallable callable, I::SummaryComponent leaf
+ ) {
+ dependsOnSummaryComponentStack(callable, I::singleton(leaf))
+ }
+
+ /**
+ * Gets a data flow I::Node corresponding to the local input or output of `call`
+ * identified by `stack`, if possible.
+ */
+ pragma[nomagic]
+ private I::Node evaluateSummaryComponentStackLocal(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack stack
+ ) {
+ exists(I::SummaryComponent component |
+ dependsOnSummaryComponentStackLeaf(callable, component) and
+ stack = I::singleton(component) and
+ call = I::callTo(callable) and
+ result = evaluateSummaryComponentLocal(call, component)
+ )
+ or
+ exists(I::Node prev, I::SummaryComponent head, I::SummaryComponentStack tail |
+ prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
+ dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
+ pragma[only_bind_out](tail)) and
+ stack = I::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
+ |
+ result = I::parameterOf(prev, head)
+ or
+ result = I::returnOf(prev, head)
+ or
+ I::componentLevelStep(head) and
+ result = prev
+ )
+ }
+
+ // Implement Output
+ predicate levelStepNoCall(I::Node nodeFrom, I::Node nodeTo) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ callable.propagatesFlow(input, output, true) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicLoadStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasLoadSummary(callable, content, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicStoreStep(I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent content) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasStoreSummary(callable, content, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicLoadStoreStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContent loadContent,
+ I::TypeTrackerContent storeContent
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicWithoutContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+
+ predicate basicWithContentStep(
+ I::Node nodeFrom, I::Node nodeTo, I::TypeTrackerContentFilter filter
+ ) {
+ exists(
+ I::SummarizedCallable callable, I::Node call, I::SummaryComponentStack input,
+ I::SummaryComponentStack output
+ |
+ hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
+ pragma[only_bind_into](output)) and
+ call = I::callTo(callable) and
+ nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
+ nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
+ )
+ }
+}
diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
index 55ec26258d6..4f17302e72b 100644
--- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
+++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll
@@ -112,15 +112,7 @@ predicate levelStepCall(Node nodeFrom, Node nodeTo) {
/** Holds if there is a level step from `nodeFrom` to `nodeTo`, which does not depend on the call graph. */
pragma[nomagic]
predicate levelStepNoCall(Node nodeFrom, Node nodeTo) {
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- callable.propagatesFlow(input, output, true) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::levelStepNoCall(nodeFrom, nodeTo)
or
localFieldStep(nodeFrom, nodeTo)
}
@@ -290,16 +282,7 @@ predicate returnStep(Node nodeFrom, Node nodeTo) {
predicate basicStoreStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet contents) {
storeStepIntoSourceNode(nodeFrom, nodeTo, contents)
or
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- hasStoreSummary(callable, contents, pragma[only_bind_into](input),
- pragma[only_bind_into](output)) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::basicStoreStep(nodeFrom, nodeTo, contents)
}
/**
@@ -333,15 +316,7 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content
nodeTo.asExpr() = call
)
or
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- hasLoadSummary(callable, contents, pragma[only_bind_into](input), pragma[only_bind_into](output)) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::basicLoadStep(nodeFrom, nodeTo, contents)
}
/**
@@ -350,48 +325,21 @@ predicate basicLoadStep(Node nodeFrom, Node nodeTo, DataFlow::ContentSet content
predicate basicLoadStoreStep(
Node nodeFrom, Node nodeTo, DataFlow::ContentSet loadContent, DataFlow::ContentSet storeContent
) {
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- hasLoadStoreSummary(callable, loadContent, storeContent, pragma[only_bind_into](input),
- pragma[only_bind_into](output)) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::basicLoadStoreStep(nodeFrom, nodeTo, loadContent, storeContent)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` but block flow of contents matched by `filter` through here.
*/
predicate basicWithoutContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- hasWithoutContentSummary(callable, filter, pragma[only_bind_into](input),
- pragma[only_bind_into](output)) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::basicWithoutContentStep(nodeFrom, nodeTo, filter)
}
/**
* Holds if type-tracking should step from `nodeFrom` to `nodeTo` if inside a content matched by `filter`.
*/
predicate basicWithContentStep(Node nodeFrom, Node nodeTo, ContentFilter filter) {
- exists(
- SummarizedCallable callable, DataFlowPublic::CallNode call, SummaryComponentStack input,
- SummaryComponentStack output
- |
- hasWithContentSummary(callable, filter, pragma[only_bind_into](input),
- pragma[only_bind_into](output)) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- nodeFrom = evaluateSummaryComponentStackLocal(callable, call, input) and
- nodeTo = evaluateSummaryComponentStackLocal(callable, call, output)
- )
+ TypeTrackerSummaryFlow::basicWithContentStep(nodeFrom, nodeTo, filter)
}
/**
@@ -403,121 +351,6 @@ class Boolean extends boolean {
private import SummaryComponentStack
-pragma[nomagic]
-private predicate hasStoreSummary(
- SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input,
- SummaryComponentStack output
-) {
- not isNonLocal(input.head()) and
- not isNonLocal(output.head()) and
- (
- callable.propagatesFlow(input, push(SummaryComponent::content(contents), output), true)
- or
- // Allow the input to start with an arbitrary WithoutContent[X].
- // Since type-tracking only tracks one content deep, and we're about to store into another content,
- // we're already preventing the input from being in a content.
- callable
- .propagatesFlow(push(SummaryComponent::withoutContent(_), input),
- push(SummaryComponent::content(contents), output), true)
- )
-}
-
-pragma[nomagic]
-private predicate hasLoadSummary(
- SummarizedCallable callable, DataFlow::ContentSet contents, SummaryComponentStack input,
- SummaryComponentStack output
-) {
- callable.propagatesFlow(push(SummaryComponent::content(contents), input), output, true) and
- not isNonLocal(input.head()) and
- not isNonLocal(output.head())
-}
-
-pragma[nomagic]
-private predicate hasLoadStoreSummary(
- SummarizedCallable callable, DataFlow::ContentSet loadContents,
- DataFlow::ContentSet storeContents, SummaryComponentStack input, SummaryComponentStack output
-) {
- callable
- .propagatesFlow(push(SummaryComponent::content(loadContents), input),
- push(SummaryComponent::content(storeContents), output), true) and
- not isNonLocal(input.head()) and
- not isNonLocal(output.head())
-}
-
-/**
- * Gets a content filter to use for a `WithoutContent[content]` step, or has no result if
- * the step should be treated as ordinary flow.
- *
- * `WithoutContent` is often used to perform strong updates on individual collection elements, but for
- * type-tracking this is rarely beneficial and quite expensive. However, `WithoutContent` can be quite useful
- * for restricting the type of an object, and in these cases we translate it to a filter.
- */
-private ContentFilter getFilterFromWithoutContentStep(DataFlow::ContentSet content) {
- (
- content.isAnyElement()
- or
- content.isElementLowerBoundOrUnknown(_)
- or
- content.isElementOfTypeOrUnknown(_)
- or
- content.isSingleton(any(DataFlow::Content::UnknownElementContent c))
- ) and
- result = MkElementFilter()
-}
-
-pragma[nomagic]
-private predicate hasWithoutContentSummary(
- SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input,
- SummaryComponentStack output
-) {
- exists(DataFlow::ContentSet content |
- callable.propagatesFlow(push(SummaryComponent::withoutContent(content), input), output, true) and
- filter = getFilterFromWithoutContentStep(content) and
- not isNonLocal(input.head()) and
- not isNonLocal(output.head()) and
- input != output
- )
-}
-
-/**
- * Gets a content filter to use for a `WithContent[content]` step, or has no result if
- * the step cannot be handled by type-tracking.
- *
- * `WithContent` is often used to perform strong updates on individual collection elements (or rather
- * to preserve those that didn't get updated). But for type-tracking this is rarely beneficial and quite expensive.
- * However, `WithContent` can be quite useful for restricting the type of an object, and in these cases we translate it to a filter.
- */
-private ContentFilter getFilterFromWithContentStep(DataFlow::ContentSet content) {
- (
- content.isAnyElement()
- or
- content.isElementLowerBound(_)
- or
- content.isElementLowerBoundOrUnknown(_)
- or
- content.isElementOfType(_)
- or
- content.isElementOfTypeOrUnknown(_)
- or
- content.isSingleton(any(DataFlow::Content::ElementContent c))
- ) and
- result = MkElementFilter()
-}
-
-pragma[nomagic]
-private predicate hasWithContentSummary(
- SummarizedCallable callable, ContentFilter filter, SummaryComponentStack input,
- SummaryComponentStack output
-) {
- exists(DataFlow::ContentSet content |
- callable.propagatesFlow(push(SummaryComponent::withContent(content), input), output, true) and
- filter = getFilterFromWithContentStep(content) and
- not isNonLocal(input.head()) and
- not isNonLocal(output.head()) and
- input != output
- )
-}
-
/**
* Holds if the given component can't be evaluated by `evaluateSummaryComponentStackLocal`.
*/
@@ -528,112 +361,129 @@ predicate isNonLocal(SummaryComponent component) {
component = SC::withContent(_)
}
-/**
- * Gets a data flow node corresponding an argument or return value of `call`,
- * as specified by `component`.
- */
-bindingset[call, component]
-private DataFlow::Node evaluateSummaryComponentLocal(
- DataFlow::CallNode call, SummaryComponent component
-) {
- exists(DataFlowDispatch::ParameterPosition pos |
- component = SummaryComponent::argument(pos) and
- argumentPositionMatch(call.asExpr(), result, pos)
- )
- or
- component = SummaryComponent::return() and
- result = call
-}
+private import SummaryTypeTracker as SummaryTypeTracker
+private import codeql.ruby.dataflow.FlowSummary as FlowSummary
-/**
- * Holds if `callable` is relevant for type-tracking and we therefore want `stack` to
- * be evaluated locally at its call sites.
- */
-pragma[nomagic]
-private predicate dependsOnSummaryComponentStack(
- SummarizedCallable callable, SummaryComponentStack stack
-) {
- exists(callable.getACallSimple()) and
- (
- callable.propagatesFlow(stack, _, true)
- or
- callable.propagatesFlow(_, stack, true)
- or
- // include store summaries as they may skip an initial step at the input
- hasStoreSummary(callable, _, stack, _)
- )
- or
- dependsOnSummaryComponentStackCons(callable, _, stack)
-}
+module SummaryTypeTrackerInput implements SummaryTypeTracker::Input {
+ // Dataflow nodes
+ class Node = DataFlow::Node;
-pragma[nomagic]
-private predicate dependsOnSummaryComponentStackCons(
- SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail
-) {
- dependsOnSummaryComponentStack(callable, SCS::push(head, tail))
-}
+ // Content
+ class TypeTrackerContent = DataFlowPublic::ContentSet;
-pragma[nomagic]
-private predicate dependsOnSummaryComponentStackConsLocal(
- SummarizedCallable callable, SummaryComponent head, SummaryComponentStack tail
-) {
- dependsOnSummaryComponentStackCons(callable, head, tail) and
- not isNonLocal(head)
-}
+ class TypeTrackerContentFilter = ContentFilter;
-pragma[nomagic]
-private predicate dependsOnSummaryComponentStackLeaf(
- SummarizedCallable callable, SummaryComponent leaf
-) {
- dependsOnSummaryComponentStack(callable, SCS::singleton(leaf))
-}
+ TypeTrackerContentFilter getFilterFromWithoutContentStep(TypeTrackerContent content) {
+ (
+ content.isAnyElement()
+ or
+ content.isElementLowerBoundOrUnknown(_)
+ or
+ content.isElementOfTypeOrUnknown(_)
+ or
+ content.isSingleton(any(DataFlow::Content::UnknownElementContent c))
+ ) and
+ result = MkElementFilter()
+ }
-/**
- * Gets a data flow node corresponding to the local input or output of `call`
- * identified by `stack`, if possible.
- */
-pragma[nomagic]
-private DataFlow::Node evaluateSummaryComponentStackLocal(
- SummarizedCallable callable, DataFlow::CallNode call, SummaryComponentStack stack
-) {
- exists(SummaryComponent component |
- dependsOnSummaryComponentStackLeaf(callable, component) and
- stack = SCS::singleton(component) and
- call.asExpr().getExpr() = callable.getACallSimple() and
- result = evaluateSummaryComponentLocal(call, component)
- )
- or
- exists(DataFlow::Node prev, SummaryComponent head, SummaryComponentStack tail |
- prev = evaluateSummaryComponentStackLocal(callable, call, tail) and
- dependsOnSummaryComponentStackConsLocal(callable, pragma[only_bind_into](head),
- pragma[only_bind_out](tail)) and
- stack = SCS::push(pragma[only_bind_out](head), pragma[only_bind_out](tail))
- |
+ TypeTrackerContentFilter getFilterFromWithContentStep(TypeTrackerContent content) {
+ (
+ content.isAnyElement()
+ or
+ content.isElementLowerBound(_)
+ or
+ content.isElementLowerBoundOrUnknown(_)
+ or
+ content.isElementOfType(_)
+ or
+ content.isElementOfTypeOrUnknown(_)
+ or
+ content.isSingleton(any(DataFlow::Content::ElementContent c))
+ ) and
+ result = MkElementFilter()
+ }
+
+ // Summaries and their stacks
+ class SummaryComponent = FlowSummary::SummaryComponent;
+
+ class SummaryComponentStack = FlowSummary::SummaryComponentStack;
+
+ SummaryComponentStack singleton(SummaryComponent component) {
+ result = FlowSummary::SummaryComponentStack::singleton(component)
+ }
+
+ SummaryComponentStack push(SummaryComponent component, SummaryComponentStack stack) {
+ result = FlowSummary::SummaryComponentStack::push(component, stack)
+ }
+
+ // Relating content to summaries
+ SummaryComponent content(TypeTrackerContent contents) {
+ result = FlowSummary::SummaryComponent::content(contents)
+ }
+
+ SummaryComponent withoutContent(TypeTrackerContent contents) {
+ result = FlowSummary::SummaryComponent::withoutContent(contents)
+ }
+
+ SummaryComponent withContent(TypeTrackerContent contents) {
+ result = FlowSummary::SummaryComponent::withContent(contents)
+ }
+
+ SummaryComponent return() { result = FlowSummary::SummaryComponent::return() }
+
+ // Callables
+ class SummarizedCallable = FlowSummary::SummarizedCallable;
+
+ // Relating nodes to summaries
+ Node argumentOf(Node call, SummaryComponent arg) {
+ exists(DataFlowDispatch::ParameterPosition pos |
+ arg = SummaryComponent::argument(pos) and
+ argumentPositionMatch(call.asExpr(), result, pos)
+ )
+ }
+
+ Node parameterOf(Node callable, SummaryComponent param) {
exists(
DataFlowDispatch::ArgumentPosition apos, DataFlowDispatch::ParameterPosition ppos,
DataFlowPrivate::ParameterNodeImpl p
|
- head = SummaryComponent::parameter(apos) and
+ param = SummaryComponent::parameter(apos) and
DataFlowDispatch::parameterMatch(ppos, apos) and
- p.isSourceParameterOf(prev.asExpr().getExpr(), ppos) and
+ p.isSourceParameterOf(callable.asExpr().getExpr(), ppos) and
// We need to include both `p` and the SSA definition for `p`, since in type-tracking
// the step from `p` to the SSA definition is considered a call step.
result =
[p.(DataFlow::Node), DataFlowPrivate::LocalFlow::getParameterDefNode(p.getParameter())]
)
- or
+ }
+
+ Node returnOf(Node callable, SummaryComponent return) {
exists(DataFlowPrivate::SynthReturnNode ret |
- head = SummaryComponent::return() and
- ret.getCfgScope() = prev.asExpr().getExpr() and
+ return = SummaryComponent::return() and
+ ret.getCfgScope() = callable.asExpr().getExpr() and
// We need to include both `ret` and `ret.getAnInput()`, since in type-tracking
// the step from `ret.getAnInput()` to `ret` is considered a return step.
result = [ret.(DataFlow::Node), ret.getAnInput()]
)
- or
- exists(DataFlow::ContentSet content |
- head = SummaryComponent::withoutContent(content) and
- not exists(getFilterFromWithoutContentStep(content)) and
- result = prev
+ }
+
+ // Specific summary handling
+ predicate componentLevelStep(SummaryComponent component) {
+ exists(TypeTrackerContent content |
+ component = SummaryComponent::withoutContent(content) and
+ not exists(getFilterFromWithoutContentStep(content))
)
- )
+ }
+
+ pragma[nomagic]
+ predicate isNonLocal(SummaryComponent component) {
+ component = SC::content(_)
+ or
+ component = SC::withContent(_)
+ }
+
+ // Relating callables to nodes
+ Node callTo(SummarizedCallable callable) { result.asExpr().getExpr() = callable.getACallSimple() }
}
+
+module TypeTrackerSummaryFlow = SummaryTypeTracker::SummaryFlow;
From 820b5f235eba6c92251dde37ad2f052092648c75 Mon Sep 17 00:00:00 2001
From: Rasmus Lerchedahl Petersen
Date: Tue, 30 May 2023 13:36:10 +0200
Subject: [PATCH 180/813] python: add change note
---
.../2023-05-30-typetracking-via-flow-summaries.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md
diff --git a/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md b/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md
new file mode 100644
index 00000000000..11c01629987
--- /dev/null
+++ b/python/ql/lib/change-notes/2023-05-30-typetracking-via-flow-summaries.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Type tracking is now aware of flow summaries. This leads to a richer API graph, and may lead to more results in some queries.
From 560aa43953a14843f0661420c40f13c1c66b0dae Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Tue, 30 May 2023 14:20:17 +0100
Subject: [PATCH 181/813] Swift: Repair for AccountID / AccountKey.
---
swift/ql/lib/codeql/swift/security/SensitiveExprs.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
index 71a250229e7..d750e673121 100644
--- a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
+++ b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
@@ -35,7 +35,7 @@ class SensitiveCredential extends SensitiveDataType, TCredential {
result = HeuristicNames::maybeSensitiveRegexp(classification)
)
or
- result = "(?is).*(license.?key).*"
+ result = "(?is).*(account|accnt|license).?(id|key).*"
}
}
From 00e4c455b5bd8750776283edd0aa97585ec28842 Mon Sep 17 00:00:00 2001
From: Taus
Date: Tue, 30 May 2023 16:11:30 +0200
Subject: [PATCH 182/813] Update MaD Declarations after Triage
---
java/ql/lib/change-notes/2023-05-30-new-models.md | 6 ++++++
java/ql/lib/ext/okhttp3.model.yml | 2 ++
2 files changed, 8 insertions(+)
create mode 100644 java/ql/lib/change-notes/2023-05-30-new-models.md
diff --git a/java/ql/lib/change-notes/2023-05-30-new-models.md b/java/ql/lib/change-notes/2023-05-30-new-models.md
new file mode 100644
index 00000000000..24e7563d727
--- /dev/null
+++ b/java/ql/lib/change-notes/2023-05-30-new-models.md
@@ -0,0 +1,6 @@
+---
+category: minorAnalysis
+---
+* Added models for the following packages:
+
+ * okhttp3
diff --git a/java/ql/lib/ext/okhttp3.model.yml b/java/ql/lib/ext/okhttp3.model.yml
index 21563331656..d5f38bcee57 100644
--- a/java/ql/lib/ext/okhttp3.model.yml
+++ b/java/ql/lib/ext/okhttp3.model.yml
@@ -3,6 +3,8 @@ extensions:
pack: codeql/java-all
extensible: sinkModel
data:
+ - ["okhttp3", "OkHttpClient", True, "newCall", "(Request)", "", "Argument[0]", "open-url", "ai-manual"]
+ - ["okhttp3", "OkHttpClient", True, "newWebSocket", "(Request,WebSocketListener)", "", "Argument[0]", "open-url", "ai-manual"]
- ["okhttp3", "Request", True, "Request", "", "", "Argument[0]", "open-url", "manual"]
- ["okhttp3", "Request$Builder", True, "url", "", "", "Argument[0]", "open-url", "manual"]
- addsTo:
From f00b29d3d29c0de4c0a79117f667ec987715a9ce Mon Sep 17 00:00:00 2001
From: Mathias Vorreiter Pedersen
Date: Fri, 26 May 2023 12:23:56 -0700
Subject: [PATCH 183/813] C++: The small-string optimization commonly used
inside 'std::string' is causing a lot of FPs. Let's exclude this for now to
reduce the number of results for this query.
---
.../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 1 +
1 file changed, 1 insertion(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
index 735375870ea..aa0358a99ad 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
@@ -87,6 +87,7 @@ predicate arrayTypeHasSizes(ArrayType arr, int baseTypeSize, int arraySize) {
predicate pointerArithOverflow0(
PointerArithmeticInstruction pai, Field f, int size, int bound, int delta
) {
+ not f.getNamespace() instanceof StdNamespace and
arrayTypeHasSizes(f.getUnspecifiedType(), pai.getElementSize(), size) and
semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and
delta = bound - size and
From d91fa2d03810bf9234f16fb2b17f8c5069c8f8ae Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Tue, 30 May 2023 17:30:04 +0200
Subject: [PATCH 184/813] Ruby: add print-cfg query
---
.../ql/lib/ide-contextual-queries/printCfg.ql | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
create mode 100644 ruby/ql/lib/ide-contextual-queries/printCfg.ql
diff --git a/ruby/ql/lib/ide-contextual-queries/printCfg.ql b/ruby/ql/lib/ide-contextual-queries/printCfg.ql
new file mode 100644
index 00000000000..9c42fe91361
--- /dev/null
+++ b/ruby/ql/lib/ide-contextual-queries/printCfg.ql
@@ -0,0 +1,22 @@
+/**
+ * @name Print CFG
+ * @description Produces a representation of a file's Control Flow Graph.
+ * This query is used by the VS Code extension.
+ * @id rb/print-cfg
+ * @kind graph
+ * @tags ide-contextual-queries/print-cfg
+ */
+
+private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared::TestOutput
+private import codeql.IDEContextual
+
+/**
+ * Gets the source file to generate a CFG from.
+ */
+external string selectedSourceFile();
+
+class MyRelevantNode extends RelevantNode {
+ MyRelevantNode() {
+ this.getScope().getLocation().getFile() = getFileBySourceArchiveName(selectedSourceFile())
+ }
+}
From 54e011188d8b98634b44e28e923a563ebda2e9ed Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 May 2023 17:50:50 +0200
Subject: [PATCH 185/813] Formatting
---
.../code/java/frameworks/google/GsonSerializability.qll | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
index 34a333c8b11..470847f292e 100644
--- a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
@@ -13,9 +13,7 @@ import semmle.code.java.dataflow.FlowSteps
* deserialized.
*/
private class GsonReadValueMethod extends Method {
- GsonReadValueMethod() {
- this.hasQualifiedName("com.google.gson", "Gson", "fromJson")
- }
+ GsonReadValueMethod() { this.hasQualifiedName("com.google.gson", "Gson", "fromJson") }
}
/** A type whose values may be deserialized by the Gson JSON framework. */
From 977263a126e190acb52fbfce90ea9ce6e3ea0b76 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 May 2023 17:51:41 +0200
Subject: [PATCH 186/813] Use container flow for more precision
---
java/ql/lib/ext/com.google.gson.model.yml | 24 +++++++++++++----------
1 file changed, 14 insertions(+), 10 deletions(-)
diff --git a/java/ql/lib/ext/com.google.gson.model.yml b/java/ql/lib/ext/com.google.gson.model.yml
index b867997c8df..96f5355b2dc 100644
--- a/java/ql/lib/ext/com.google.gson.model.yml
+++ b/java/ql/lib/ext/com.google.gson.model.yml
@@ -26,15 +26,19 @@ extensions:
- ["com.google.gson", "JsonElement", True, "getAsJsonPrimitive", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["com.google.gson", "JsonElement", True, "getAsString", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["com.google.gson", "JsonElement", True, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonArray", True, "add", "", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- - ["com.google.gson", "JsonArray", True, "asList", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonArray", True, "get", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonArray", True, "set", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "add", "", "", "Argument[1]", "Argument[this]", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "addProperty", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "asMap", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "entrySet", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "get", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- - ["com.google.gson", "JsonObject", True, "keySet", "", "", "Argument[this]", "ReturnValue", "taint", "manual"]
+ - ["com.google.gson", "JsonArray", True, "add", "", "", "Argument[0]", "Argument[this].Element", "value", "manual"]
+ - ["com.google.gson", "JsonArray", True, "asList", "", "", "Argument[this].Element", "ReturnValue.Element", "value", "manual"]
+ - ["com.google.gson", "JsonArray", True, "get", "", "", "Argument[this].Element", "ReturnValue", "value", "manual"]
+ - ["com.google.gson", "JsonArray", True, "set", "", "", "Argument[1]", "Argument[this].Element", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "add", "", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "add", "", "", "Argument[1]", "Argument[this].MapValue", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "addProperty", "(String,String)", "", "Argument[0]", "Argument[this].MapKey", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "addProperty", "(String,String)", "", "Argument[1]", "Argument[this].MapValue", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "asMap", "", "", "Argument[this].MapKey", "ReturnValue.MapKey", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "asMap", "", "", "Argument[this].MapValue", "ReturnValue.MapValue", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "entrySet", "", "", "Argument[this].MapKey", "ReturnValue.Element.MapKey", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "entrySet", "", "", "Argument[this].MapKey", "ReturnValue.Element.MapValue", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "get", "", "", "Argument[this].MapValue", "ReturnValue", "value", "manual"]
+ - ["com.google.gson", "JsonObject", True, "keySet", "", "", "Argument[this].MapKey", "ReturnValue.Element", "value", "manual"]
- ["com.google.gson", "JsonPrimitive", True, "JsonPrimitive", "(Character)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
- ["com.google.gson", "JsonPrimitive", True, "JsonPrimitive", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"]
From d3d67f0fb07406242913e9443398a9fa2238c8bf Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 May 2023 17:52:00 +0200
Subject: [PATCH 187/813] Add tests & stubs
---
.../dataflow/taint-gson/Test.java | 38 ++
.../dataflow/taint-gson/dataFlow.expected | 0
.../dataflow/taint-gson/dataFlow.ql | 2 +
.../library-tests/dataflow/taint-gson/options | 1 +
.../library-tests/frameworks/gson/Test.java | 468 ++++++++++++++++++
.../library-tests/frameworks/gson/options | 1 +
.../frameworks/gson/test.expected | 0
.../library-tests/frameworks/gson/test.ql | 2 +
.../com/google/gson/ExclusionStrategy.java | 11 +
.../com/google/gson/FieldAttributes.java | 22 +
.../com/google/gson/FieldNamingPolicy.java | 10 +
.../com/google/gson/FieldNamingStrategy.java | 10 +
.../gson-2.8.6/com/google/gson/Gson.java | 81 +--
.../com/google/gson/GsonBuilder.java | 135 ++---
.../gson-2.8.6/com/google/gson/JsonArray.java | 45 ++
.../com/google/gson/JsonElement.java | 37 ++
.../gson-2.8.6/com/google/gson/JsonNull.java | 14 +
.../com/google/gson/JsonObject.java | 33 ++
.../com/google/gson/JsonPrimitive.java | 34 ++
.../google/gson/LongSerializationPolicy.java | 24 +
.../google/gson/ReflectionAccessFilter.java | 18 +
.../com/google/gson/ToNumberStrategy.java | 10 +
.../com/google/gson/TypeAdapter.java | 137 +----
.../com/google/gson/TypeAdapterFactory.java | 30 +-
.../com/google/gson/internal/Excluder.java | 25 +
.../com/google/gson/reflect/TypeToken.java | 64 +--
.../com/google/gson/stream/JsonReader.java | 89 ++--
.../com/google/gson/stream/JsonToken.java | 10 +
.../com/google/gson/stream/JsonWriter.java | 36 ++
29 files changed, 1008 insertions(+), 379 deletions(-)
create mode 100644 java/ql/test/library-tests/dataflow/taint-gson/Test.java
create mode 100644 java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected
create mode 100644 java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql
create mode 100644 java/ql/test/library-tests/dataflow/taint-gson/options
create mode 100644 java/ql/test/library-tests/frameworks/gson/Test.java
create mode 100644 java/ql/test/library-tests/frameworks/gson/options
create mode 100644 java/ql/test/library-tests/frameworks/gson/test.expected
create mode 100644 java/ql/test/library-tests/frameworks/gson/test.ql
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/ExclusionStrategy.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldAttributes.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingPolicy.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingStrategy.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonArray.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonElement.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonNull.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonObject.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonPrimitive.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/LongSerializationPolicy.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/ReflectionAccessFilter.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/ToNumberStrategy.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/internal/Excluder.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonToken.java
create mode 100644 java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonWriter.java
diff --git a/java/ql/test/library-tests/dataflow/taint-gson/Test.java b/java/ql/test/library-tests/dataflow/taint-gson/Test.java
new file mode 100644
index 00000000000..82ca2388bbe
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/taint-gson/Test.java
@@ -0,0 +1,38 @@
+import com.google.gson.Gson;
+
+public class Test {
+ public static class Potato {
+ private String name;
+ private Potato inner;
+ private Object object;
+
+ private String getName() {
+ return name;
+ }
+
+ private Potato getInner() {
+ return inner;
+ }
+
+ private Object getObject() {
+ return object;
+ }
+
+ }
+
+ public static String source() {
+ return "";
+ }
+
+ public static void sink(Object any) {}
+
+ public static void gsonfromJson() throws Exception {
+ String s = source();
+ Potato tainted = new Gson().fromJson(s, Potato.class);
+ sink(tainted); // $ hasTaintFlow
+ sink(tainted.getName()); // $ hasTaintFlow
+ sink(tainted.getInner()); // $ hasTaintFlow
+ sink(tainted.getInner().getName()); // $ hasTaintFlow
+ sink(tainted.getObject()); // $ hasTaintFlow
+ }
+}
diff --git a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql
new file mode 100644
index 00000000000..5d91e4e8e26
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/taint-gson/dataFlow.ql
@@ -0,0 +1,2 @@
+import java
+import TestUtilities.InlineFlowTest
diff --git a/java/ql/test/library-tests/dataflow/taint-gson/options b/java/ql/test/library-tests/dataflow/taint-gson/options
new file mode 100644
index 00000000000..a9cce94fd94
--- /dev/null
+++ b/java/ql/test/library-tests/dataflow/taint-gson/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/gson-2.8.6
diff --git a/java/ql/test/library-tests/frameworks/gson/Test.java b/java/ql/test/library-tests/frameworks/gson/Test.java
new file mode 100644
index 00000000000..eb3e1e526f0
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/gson/Test.java
@@ -0,0 +1,468 @@
+package generatedtest;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+// Test case generated by GenerateFlowTestCase.ql
+public class Test {
+
+ K getMapKey(Map map) { return map.keySet().iterator().next(); }
+ T getElement(Iterable it) { return it.iterator().next(); }
+ V getMapValue(Map,V> map) { return map.get(null); }
+ String getMapKeyDefault(JsonObject container) { return container.keySet().iterator().next(); }
+ K getMapKeyDefault(Map.Entry container) { return container.getKey(); }
+ JsonElement getMapValueDefault(JsonObject container) { return container.get(null); }
+ V getMapValueDefault(Map.Entry,V> container) { return container.getValue(); }
+ JsonArray newWithElementDefault(String element) { JsonArray a = new JsonArray(); a.add(element); return a; }
+ JsonObject newWithMapKeyDefault(String key) { JsonObject o = new JsonObject(); o.add(key, (JsonElement) null); return o; }
+ JsonObject newWithMapValueDefault(JsonElement element) { JsonObject o = new JsonObject(); o.add(null, element); return o; }
+ Object source() { return null; }
+ void sink(Object o) { }
+
+ public void test() throws Exception {
+
+ {
+ // "com.google.gson.stream;JsonReader;false;nextName;;;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonReader in = (JsonReader)source();
+ out = in.nextName();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson.stream;JsonReader;false;nextString;;;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonReader in = (JsonReader)source();
+ out = in.nextString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Class)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Type)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (TypeToken)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ JsonReader in = (JsonReader)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Type)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ JsonReader in = (JsonReader)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (TypeToken)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ Reader in = (Reader)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Class)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ Reader in = (Reader)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Type)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ Reader in = (Reader)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (TypeToken)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ String in = (String)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Class)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ String in = (String)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (Type)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;fromJson;;;Argument[0];ReturnValue;taint;manual"
+ Object out = null;
+ String in = (String)source();
+ Gson instance = null;
+ out = instance.fromJson(in, (TypeToken)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;newJsonReader;;;Argument[0];ReturnValue;taint;manual"
+ JsonReader out = null;
+ Reader in = (Reader)source();
+ Gson instance = null;
+ out = instance.newJsonReader(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;newJsonWriter;;;Argument[0];ReturnValue;taint;manual"
+ JsonWriter out = null;
+ Writer in = (Writer)source();
+ Gson instance = null;
+ out = instance.newJsonWriter(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(JsonElement);;Argument[0];ReturnValue;taint;manual"
+ String out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ out = instance.toJson(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(JsonElement,Appendable);;Argument[0];Argument[1];taint;manual"
+ Appendable out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ instance.toJson(in, out);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(JsonElement,JsonWriter);;Argument[0];Argument[1];taint;manual"
+ JsonWriter out = null;
+ JsonElement in = (JsonElement)source();
+ Gson instance = null;
+ instance.toJson(in, out);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(Object);;Argument[0];ReturnValue;taint;manual"
+ String out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ out = instance.toJson(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(Object,Appendable);;Argument[0];Argument[1];taint;manual"
+ Appendable out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ instance.toJson(in, out);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(Object,Type);;Argument[0];ReturnValue;taint;manual"
+ String out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ out = instance.toJson(in, (Type)null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(Object,Type,Appendable);;Argument[0];Argument[2];taint;manual"
+ Appendable out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ instance.toJson(in, (Type)null, out);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJson;(Object,Type,JsonWriter);;Argument[0];Argument[2];taint;manual"
+ JsonWriter out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ instance.toJson(in, (Type)null, out);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJsonTree;(Object);;Argument[0];ReturnValue;taint;manual"
+ JsonElement out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ out = instance.toJsonTree(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;Gson;false;toJsonTree;(Object,Type);;Argument[0];ReturnValue;taint;manual"
+ JsonElement out = null;
+ Object in = (Object)source();
+ Gson instance = null;
+ out = instance.toJsonTree(in, null);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;add;;;Argument[0];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ Boolean in = (Boolean)source();
+ out.add(in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;add;;;Argument[0];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ Character in = (Character)source();
+ out.add(in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;add;;;Argument[0];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ JsonElement in = (JsonElement)source();
+ out.add(in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;add;;;Argument[0];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ Number in = (Number)source();
+ out.add(in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;add;;;Argument[0];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ String in = (String)source();
+ out.add(in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;asList;;;Argument[this].Element;ReturnValue.Element;value;manual"
+ List out = null;
+ JsonArray in = (JsonArray)newWithElementDefault((String) source());
+ out = in.asList();
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;get;;;Argument[this].Element;ReturnValue;value;manual"
+ JsonElement out = null;
+ JsonArray in = (JsonArray)newWithElementDefault((String) source());
+ out = in.get(0);
+ sink(out); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonArray;true;set;;;Argument[1];Argument[this].Element;value;manual"
+ JsonArray out = null;
+ JsonElement in = (JsonElement)source();
+ out.set(0, in);
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsByte;();;Argument[this];ReturnValue;taint;manual"
+ byte out = 0;
+ JsonArray in = (JsonArray)source();
+ out = in.getAsByte();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsByte;();;Argument[this];ReturnValue;taint;manual"
+ byte out = 0;
+ JsonElement in = (JsonElement)source();
+ out = in.getAsByte();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsByte;();;Argument[this];ReturnValue;taint;manual"
+ byte out = 0;
+ JsonPrimitive in = (JsonPrimitive)source();
+ out = in.getAsByte();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsCharacter;();;Argument[this];ReturnValue;taint;manual"
+ char out = 'a';
+ JsonArray in = (JsonArray)source();
+ out = in.getAsCharacter();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsCharacter;();;Argument[this];ReturnValue;taint;manual"
+ char out = 'a';
+ JsonElement in = (JsonElement)source();
+ out = in.getAsCharacter();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsCharacter;();;Argument[this];ReturnValue;taint;manual"
+ char out = 'a';
+ JsonPrimitive in = (JsonPrimitive)source();
+ out = in.getAsCharacter();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsJsonArray;();;Argument[this];ReturnValue;taint;manual"
+ JsonArray out = null;
+ JsonElement in = (JsonElement)source();
+ out = in.getAsJsonArray();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsJsonObject;();;Argument[this];ReturnValue;taint;manual"
+ JsonObject out = null;
+ JsonElement in = (JsonElement)source();
+ out = in.getAsJsonObject();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsJsonPrimitive;();;Argument[this];ReturnValue;taint;manual"
+ JsonPrimitive out = null;
+ JsonElement in = (JsonElement)source();
+ out = in.getAsJsonPrimitive();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsString;();;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonArray in = (JsonArray)source();
+ out = in.getAsString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsString;();;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonElement in = (JsonElement)source();
+ out = in.getAsString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;getAsString;();;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonPrimitive in = (JsonPrimitive)source();
+ out = in.getAsString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonElement;true;toString;();;Argument[this];ReturnValue;taint;manual"
+ String out = null;
+ JsonElement in = (JsonElement)source();
+ out = in.toString();
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;add;;;Argument[0];Argument[this].MapKey;value;manual"
+ JsonObject out = null;
+ String in = (String)source();
+ out.add(in, null);
+ sink(getMapKeyDefault(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;add;;;Argument[1];Argument[this].MapValue;value;manual"
+ JsonObject out = null;
+ JsonElement in = (JsonElement)source();
+ out.add(null, in);
+ sink(getMapValueDefault(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;addProperty;(String,String);;Argument[0];Argument[this].MapKey;value;manual"
+ JsonObject out = null;
+ String in = (String)source();
+ out.addProperty(in, (String)null);
+ sink(getMapKeyDefault(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;addProperty;(String,String);;Argument[1];Argument[this].MapValue;value;manual"
+ JsonObject out = null;
+ String in = (String)source();
+ out.addProperty((String)null, in);
+ sink(getMapValueDefault(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;asMap;;;Argument[this].MapKey;ReturnValue.MapKey;value;manual"
+ Map out = null;
+ JsonObject in = (JsonObject)newWithMapKeyDefault((String) source());
+ out = in.asMap();
+ sink(getMapKey(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;asMap;;;Argument[this].MapValue;ReturnValue.MapValue;value;manual"
+ Map out = null;
+ JsonObject in = (JsonObject)newWithMapValueDefault((JsonElement) source());
+ out = in.asMap();
+ sink(getMapValue(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;entrySet;;;Argument[this].MapKey;ReturnValue.Element.MapKey;value;manual"
+ Set> out = null;
+ JsonObject in = (JsonObject)newWithMapKeyDefault((String) source());
+ out = in.entrySet();
+ sink(getMapKeyDefault(getElement(out))); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;entrySet;;;Argument[this].MapKey;ReturnValue.Element.MapValue;value;manual"
+ Set> out = null;
+ JsonObject in = (JsonObject) newWithMapKeyDefault((String) source());
+ out = in.entrySet();
+ sink(getMapValueDefault(getElement(out))); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;get;;;Argument[this].MapValue;ReturnValue;value;manual"
+ JsonElement out = null;
+ JsonObject in = (JsonObject)newWithMapValueDefault((JsonElement) source());
+ out = in.get(null);
+ sink(out); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonObject;true;keySet;;;Argument[this].MapKey;ReturnValue.Element;value;manual"
+ Set out = null;
+ JsonObject in = (JsonObject)newWithMapKeyDefault((String) source());
+ out = in.keySet();
+ sink(getElement(out)); // $ hasValueFlow
+ }
+ {
+ // "com.google.gson;JsonPrimitive;true;JsonPrimitive;(Character);;Argument[0];Argument[this];taint;manual"
+ JsonPrimitive out = null;
+ Character in = (Character)source();
+ out = new JsonPrimitive(in);
+ sink(out); // $ hasTaintFlow
+ }
+ {
+ // "com.google.gson;JsonPrimitive;true;JsonPrimitive;(String);;Argument[0];Argument[this];taint;manual"
+ JsonPrimitive out = null;
+ String in = (String)source();
+ out = new JsonPrimitive(in);
+ sink(out); // $ hasTaintFlow
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/java/ql/test/library-tests/frameworks/gson/options b/java/ql/test/library-tests/frameworks/gson/options
new file mode 100644
index 00000000000..a9cce94fd94
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/gson/options
@@ -0,0 +1 @@
+//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/gson-2.8.6
diff --git a/java/ql/test/library-tests/frameworks/gson/test.expected b/java/ql/test/library-tests/frameworks/gson/test.expected
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/java/ql/test/library-tests/frameworks/gson/test.ql b/java/ql/test/library-tests/frameworks/gson/test.ql
new file mode 100644
index 00000000000..5d91e4e8e26
--- /dev/null
+++ b/java/ql/test/library-tests/frameworks/gson/test.ql
@@ -0,0 +1,2 @@
+import java
+import TestUtilities.InlineFlowTest
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/ExclusionStrategy.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ExclusionStrategy.java
new file mode 100644
index 00000000000..a1cac336243
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ExclusionStrategy.java
@@ -0,0 +1,11 @@
+// Generated automatically from com.google.gson.ExclusionStrategy for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.FieldAttributes;
+
+public interface ExclusionStrategy
+{
+ boolean shouldSkipClass(Class extends Object> p0);
+ boolean shouldSkipField(FieldAttributes p0);
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldAttributes.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldAttributes.java
new file mode 100644
index 00000000000..1db8d794976
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldAttributes.java
@@ -0,0 +1,22 @@
+// Generated automatically from com.google.gson.FieldAttributes for testing purposes
+
+package com.google.gson;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.Collection;
+
+public class FieldAttributes
+{
+ protected FieldAttributes() {}
+ public T getAnnotation(java.lang.Class p0){ return null; }
+ public Class extends Object> getDeclaredClass(){ return null; }
+ public Class extends Object> getDeclaringClass(){ return null; }
+ public Collection getAnnotations(){ return null; }
+ public FieldAttributes(Field p0){}
+ public String getName(){ return null; }
+ public String toString(){ return null; }
+ public Type getDeclaredType(){ return null; }
+ public boolean hasModifier(int p0){ return false; }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingPolicy.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingPolicy.java
new file mode 100644
index 00000000000..465703ae28a
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingPolicy.java
@@ -0,0 +1,10 @@
+// Generated automatically from com.google.gson.FieldNamingPolicy for testing purposes
+
+package com.google.gson;
+
+
+public enum FieldNamingPolicy {
+ IDENTITY, LOWER_CASE_WITH_DASHES, LOWER_CASE_WITH_DOTS, LOWER_CASE_WITH_UNDERSCORES, UPPER_CAMEL_CASE, UPPER_CAMEL_CASE_WITH_SPACES, UPPER_CASE_WITH_UNDERSCORES;
+
+ private FieldNamingPolicy() {}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingStrategy.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingStrategy.java
new file mode 100644
index 00000000000..bb3ad76d598
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/FieldNamingStrategy.java
@@ -0,0 +1,10 @@
+// Generated automatically from com.google.gson.FieldNamingStrategy for testing purposes
+
+package com.google.gson;
+
+import java.lang.reflect.Field;
+
+public interface FieldNamingStrategy
+{
+ String translateName(Field p0);
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
index a269763665b..61c29245d15 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/Gson.java
@@ -1,38 +1,53 @@
+// Generated automatically from com.google.gson.Gson for testing purposes
+
package com.google.gson;
-import java.lang.reflect.Type;
-import java.io.Reader;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.Excluder;
+import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.lang.reflect.Type;
-public final class Gson {
- public Gson() {
- }
-
- public String toJson(Object src) {
- return null;
- }
-
- public String toJson(Object src, Type typeOfSrc) {
- return null;
- }
-
- public T fromJson(String json, Class classOfT) throws JsonSyntaxException {
- return null;
- }
-
- public T fromJson(String json, Type typeOfT) throws JsonSyntaxException {
- return null;
- }
-
- public T fromJson(Reader json, Class classOfT) throws JsonSyntaxException, JsonIOException {
- return null;
- }
-
- public T fromJson(Reader json, Type typeOfT) throws JsonIOException, JsonSyntaxException {
- return null;
- }
-
- public T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
- return null;
- }
+public class Gson
+{
+ public T fromJson(JsonElement p0, Type p1){ return null; }
+ public T fromJson(JsonElement p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public T fromJson(JsonElement p0, java.lang.Class p1){ return null; }
+ public T fromJson(JsonReader p0, Type p1){ return null; }
+ public T fromJson(JsonReader p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public T fromJson(Reader p0, Type p1){ return null; }
+ public T fromJson(Reader p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public T fromJson(Reader p0, java.lang.Class p1){ return null; }
+ public T fromJson(String p0, Type p1){ return null; }
+ public T fromJson(String p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public T fromJson(String p0, java.lang.Class p1){ return null; }
+ public com.google.gson.TypeAdapter getAdapter(com.google.gson.reflect.TypeToken p0){ return null; }
+ public com.google.gson.TypeAdapter getAdapter(java.lang.Class p0){ return null; }
+ public com.google.gson.TypeAdapter getDelegateAdapter(TypeAdapterFactory p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public Excluder excluder(){ return null; }
+ public FieldNamingStrategy fieldNamingStrategy(){ return null; }
+ public Gson(){}
+ public GsonBuilder newBuilder(){ return null; }
+ public JsonElement toJsonTree(Object p0){ return null; }
+ public JsonElement toJsonTree(Object p0, Type p1){ return null; }
+ public JsonReader newJsonReader(Reader p0){ return null; }
+ public JsonWriter newJsonWriter(Writer p0){ return null; }
+ public String toJson(JsonElement p0){ return null; }
+ public String toJson(Object p0){ return null; }
+ public String toJson(Object p0, Type p1){ return null; }
+ public String toString(){ return null; }
+ public boolean htmlSafe(){ return false; }
+ public boolean serializeNulls(){ return false; }
+ public void toJson(JsonElement p0, Appendable p1){}
+ public void toJson(JsonElement p0, JsonWriter p1){}
+ public void toJson(Object p0, Appendable p1){}
+ public void toJson(Object p0, Type p1, Appendable p2){}
+ public void toJson(Object p0, Type p1, JsonWriter p2){}
}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java
index 3853cb40356..33d656b7bd7 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/GsonBuilder.java
@@ -1,99 +1,46 @@
-/*
- * Copyright (C) 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Generated automatically from com.google.gson.GsonBuilder for testing purposes
package com.google.gson;
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.FieldNamingStrategy;
+import com.google.gson.Gson;
+import com.google.gson.LongSerializationPolicy;
+import com.google.gson.ReflectionAccessFilter;
+import com.google.gson.ToNumberStrategy;
+import com.google.gson.TypeAdapterFactory;
import java.lang.reflect.Type;
-public final class GsonBuilder {
- /**
- * Creates a GsonBuilder instance that can be used to build Gson with various configuration
- * settings. GsonBuilder follows the builder pattern, and it is typically used by first
- * invoking various configuration methods to set desired options, and finally calling
- * {@link #create()}.
- */
- public GsonBuilder() {
- }
-
- /**
- * Constructs a GsonBuilder instance from a Gson instance. The newly constructed GsonBuilder
- * has the same configuration as the previously built Gson instance.
- *
- * @param gson the gson instance whose configuration should by applied to a new GsonBuilder.
- */
- GsonBuilder(Gson gson) {
- }
-
- /**
- * Configures Gson for custom serialization or deserialization. This method combines the
- * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a
- * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
- * all the required interfaces for custom serialization with Gson. If a type adapter was
- * previously registered for the specified {@code type}, it is overwritten.
- *
- *
This registers the type specified and no other types: you must manually register related
- * types! For example, applications registering {@code boolean.class} should also register {@code
- * Boolean.class}.
- *
- * @param type the type definition for the type adapter being registered
- * @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
- * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
- * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
- */
- public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
- return null;
- }
-
- /**
- * Register a factory for type adapters. Registering a factory is useful when the type
- * adapter needs to be configured based on the type of the field being processed. Gson
- * is designed to handle a large number of factories, so you should consider registering
- * them to be at par with registering an individual type adapter.
- *
- * @since 2.1
- */
- public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory factory) {
- return null;
- }
-
- /**
- * Configures Gson for custom serialization or deserialization for an inheritance type hierarchy.
- * This method combines the registration of a {@link TypeAdapter}, {@link JsonSerializer} and
- * a {@link JsonDeserializer}. If a type adapter was previously registered for the specified
- * type hierarchy, it is overridden. If a type adapter is registered for a specific type in
- * the type hierarchy, it will be invoked instead of the one registered for the type hierarchy.
- *
- * @param baseType the class definition for the type adapter being registered for the base class
- * or interface
- * @param typeAdapter This object must implement at least one of {@link TypeAdapter},
- * {@link JsonSerializer} or {@link JsonDeserializer} interfaces.
- * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
- * @since 1.7
- */
- public GsonBuilder registerTypeHierarchyAdapter(Class> baseType, Object typeAdapter) {
- return null;
- }
-
- /**
- * Creates a {@link Gson} instance based on the current configuration. This method is free of
- * side-effects to this {@code GsonBuilder} instance and hence can be called multiple times.
- *
- * @return an instance of Gson configured with the options currently set in this builder
- */
- public Gson create() {
- return null;
- }
-}
\ No newline at end of file
+public class GsonBuilder
+{
+ public Gson create(){ return null; }
+ public GsonBuilder addDeserializationExclusionStrategy(ExclusionStrategy p0){ return null; }
+ public GsonBuilder addReflectionAccessFilter(ReflectionAccessFilter p0){ return null; }
+ public GsonBuilder addSerializationExclusionStrategy(ExclusionStrategy p0){ return null; }
+ public GsonBuilder disableHtmlEscaping(){ return null; }
+ public GsonBuilder disableInnerClassSerialization(){ return null; }
+ public GsonBuilder disableJdkUnsafe(){ return null; }
+ public GsonBuilder enableComplexMapKeySerialization(){ return null; }
+ public GsonBuilder excludeFieldsWithModifiers(int... p0){ return null; }
+ public GsonBuilder excludeFieldsWithoutExposeAnnotation(){ return null; }
+ public GsonBuilder generateNonExecutableJson(){ return null; }
+ public GsonBuilder registerTypeAdapter(Type p0, Object p1){ return null; }
+ public GsonBuilder registerTypeAdapterFactory(TypeAdapterFactory p0){ return null; }
+ public GsonBuilder registerTypeHierarchyAdapter(Class extends Object> p0, Object p1){ return null; }
+ public GsonBuilder serializeNulls(){ return null; }
+ public GsonBuilder serializeSpecialFloatingPointValues(){ return null; }
+ public GsonBuilder setDateFormat(String p0){ return null; }
+ public GsonBuilder setDateFormat(int p0){ return null; }
+ public GsonBuilder setDateFormat(int p0, int p1){ return null; }
+ public GsonBuilder setExclusionStrategies(ExclusionStrategy... p0){ return null; }
+ public GsonBuilder setFieldNamingPolicy(FieldNamingPolicy p0){ return null; }
+ public GsonBuilder setFieldNamingStrategy(FieldNamingStrategy p0){ return null; }
+ public GsonBuilder setLenient(){ return null; }
+ public GsonBuilder setLongSerializationPolicy(LongSerializationPolicy p0){ return null; }
+ public GsonBuilder setNumberToNumberStrategy(ToNumberStrategy p0){ return null; }
+ public GsonBuilder setObjectToNumberStrategy(ToNumberStrategy p0){ return null; }
+ public GsonBuilder setPrettyPrinting(){ return null; }
+ public GsonBuilder setVersion(double p0){ return null; }
+ public GsonBuilder(){}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonArray.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonArray.java
new file mode 100644
index 00000000000..c4fbae6bc1f
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonArray.java
@@ -0,0 +1,45 @@
+// Generated automatically from com.google.gson.JsonArray for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonElement;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+
+public class JsonArray extends JsonElement implements Iterable
+{
+ public BigDecimal getAsBigDecimal(){ return null; }
+ public BigInteger getAsBigInteger(){ return null; }
+ public Iterator iterator(){ return null; }
+ public JsonArray deepCopy(){ return null; }
+ public JsonArray(){}
+ public JsonArray(int p0){}
+ public JsonElement get(int p0){ return null; }
+ public JsonElement remove(int p0){ return null; }
+ public JsonElement set(int p0, JsonElement p1){ return null; }
+ public List asList(){ return null; }
+ public Number getAsNumber(){ return null; }
+ public String getAsString(){ return null; }
+ public boolean contains(JsonElement p0){ return false; }
+ public boolean equals(Object p0){ return false; }
+ public boolean getAsBoolean(){ return false; }
+ public boolean isEmpty(){ return false; }
+ public boolean remove(JsonElement p0){ return false; }
+ public byte getAsByte(){ return 0; }
+ public char getAsCharacter(){ return '0'; }
+ public double getAsDouble(){ return 0; }
+ public float getAsFloat(){ return 0; }
+ public int getAsInt(){ return 0; }
+ public int hashCode(){ return 0; }
+ public int size(){ return 0; }
+ public long getAsLong(){ return 0; }
+ public short getAsShort(){ return 0; }
+ public void add(Boolean p0){}
+ public void add(Character p0){}
+ public void add(JsonElement p0){}
+ public void add(Number p0){}
+ public void add(String p0){}
+ public void addAll(JsonArray p0){}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonElement.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonElement.java
new file mode 100644
index 00000000000..592fce2b672
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonElement.java
@@ -0,0 +1,37 @@
+// Generated automatically from com.google.gson.JsonElement for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+abstract public class JsonElement
+{
+ public BigDecimal getAsBigDecimal(){ return null; }
+ public BigInteger getAsBigInteger(){ return null; }
+ public JsonArray getAsJsonArray(){ return null; }
+ public JsonElement(){}
+ public JsonNull getAsJsonNull(){ return null; }
+ public JsonObject getAsJsonObject(){ return null; }
+ public JsonPrimitive getAsJsonPrimitive(){ return null; }
+ public Number getAsNumber(){ return null; }
+ public String getAsString(){ return null; }
+ public String toString(){ return null; }
+ public abstract JsonElement deepCopy();
+ public boolean getAsBoolean(){ return false; }
+ public boolean isJsonArray(){ return false; }
+ public boolean isJsonNull(){ return false; }
+ public boolean isJsonObject(){ return false; }
+ public boolean isJsonPrimitive(){ return false; }
+ public byte getAsByte(){ return 0; }
+ public char getAsCharacter(){ return '0'; }
+ public double getAsDouble(){ return 0; }
+ public float getAsFloat(){ return 0; }
+ public int getAsInt(){ return 0; }
+ public long getAsLong(){ return 0; }
+ public short getAsShort(){ return 0; }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonNull.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonNull.java
new file mode 100644
index 00000000000..e38275991eb
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonNull.java
@@ -0,0 +1,14 @@
+// Generated automatically from com.google.gson.JsonNull for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonElement;
+
+public class JsonNull extends JsonElement
+{
+ public JsonNull deepCopy(){ return null; }
+ public JsonNull(){}
+ public boolean equals(Object p0){ return false; }
+ public int hashCode(){ return 0; }
+ public static JsonNull INSTANCE = null;
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonObject.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonObject.java
new file mode 100644
index 00000000000..a37b5455b51
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonObject.java
@@ -0,0 +1,33 @@
+// Generated automatically from com.google.gson.JsonObject for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonPrimitive;
+import java.util.Map;
+import java.util.Set;
+
+public class JsonObject extends JsonElement
+{
+ public JsonArray getAsJsonArray(String p0){ return null; }
+ public JsonElement get(String p0){ return null; }
+ public JsonElement remove(String p0){ return null; }
+ public JsonObject deepCopy(){ return null; }
+ public JsonObject getAsJsonObject(String p0){ return null; }
+ public JsonObject(){}
+ public JsonPrimitive getAsJsonPrimitive(String p0){ return null; }
+ public Map asMap(){ return null; }
+ public Set> entrySet(){ return null; }
+ public Set keySet(){ return null; }
+ public boolean equals(Object p0){ return false; }
+ public boolean has(String p0){ return false; }
+ public boolean isEmpty(){ return false; }
+ public int hashCode(){ return 0; }
+ public int size(){ return 0; }
+ public void add(String p0, JsonElement p1){}
+ public void addProperty(String p0, Boolean p1){}
+ public void addProperty(String p0, Character p1){}
+ public void addProperty(String p0, Number p1){}
+ public void addProperty(String p0, String p1){}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonPrimitive.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonPrimitive.java
new file mode 100644
index 00000000000..21ec07c4246
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/JsonPrimitive.java
@@ -0,0 +1,34 @@
+// Generated automatically from com.google.gson.JsonPrimitive for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonElement;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+public class JsonPrimitive extends JsonElement
+{
+ protected JsonPrimitive() {}
+ public BigDecimal getAsBigDecimal(){ return null; }
+ public BigInteger getAsBigInteger(){ return null; }
+ public JsonPrimitive deepCopy(){ return null; }
+ public JsonPrimitive(Boolean p0){}
+ public JsonPrimitive(Character p0){}
+ public JsonPrimitive(Number p0){}
+ public JsonPrimitive(String p0){}
+ public Number getAsNumber(){ return null; }
+ public String getAsString(){ return null; }
+ public boolean equals(Object p0){ return false; }
+ public boolean getAsBoolean(){ return false; }
+ public boolean isBoolean(){ return false; }
+ public boolean isNumber(){ return false; }
+ public boolean isString(){ return false; }
+ public byte getAsByte(){ return 0; }
+ public char getAsCharacter(){ return '0'; }
+ public double getAsDouble(){ return 0; }
+ public float getAsFloat(){ return 0; }
+ public int getAsInt(){ return 0; }
+ public int hashCode(){ return 0; }
+ public long getAsLong(){ return 0; }
+ public short getAsShort(){ return 0; }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/LongSerializationPolicy.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/LongSerializationPolicy.java
new file mode 100644
index 00000000000..0452deec4b9
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/LongSerializationPolicy.java
@@ -0,0 +1,24 @@
+// Generated automatically from com.google.gson.LongSerializationPolicy for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.JsonElement;
+
+public enum LongSerializationPolicy {
+ DEFAULT {
+ @Override
+ public JsonElement serialize(Long p0) {
+ return null;
+ }
+ },
+ STRING {
+ @Override
+ public JsonElement serialize(Long p0) {
+ return null;
+ }
+ };
+
+ private LongSerializationPolicy() {}
+
+ public abstract JsonElement serialize(Long p0);
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/ReflectionAccessFilter.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ReflectionAccessFilter.java
new file mode 100644
index 00000000000..ff91f103f62
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ReflectionAccessFilter.java
@@ -0,0 +1,18 @@
+// Generated automatically from com.google.gson.ReflectionAccessFilter for testing purposes
+
+package com.google.gson;
+
+
+public interface ReflectionAccessFilter
+{
+ ReflectionAccessFilter.FilterResult check(Class extends Object> p0);
+ static ReflectionAccessFilter BLOCK_ALL_ANDROID = null;
+ static ReflectionAccessFilter BLOCK_ALL_JAVA = null;
+ static ReflectionAccessFilter BLOCK_ALL_PLATFORM = null;
+ static ReflectionAccessFilter BLOCK_INACCESSIBLE_JAVA = null;
+ static public enum FilterResult
+ {
+ ALLOW, BLOCK_ALL, BLOCK_INACCESSIBLE, INDECISIVE;
+ private FilterResult() {}
+ }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/ToNumberStrategy.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ToNumberStrategy.java
new file mode 100644
index 00000000000..1c6ccb23111
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/ToNumberStrategy.java
@@ -0,0 +1,10 @@
+// Generated automatically from com.google.gson.ToNumberStrategy for testing purposes
+
+package com.google.gson;
+
+import com.google.gson.stream.JsonReader;
+
+public interface ToNumberStrategy
+{
+ Number readNumber(JsonReader p0);
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java
index 73e6ef993b7..cdd0d1185b1 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapter.java
@@ -1,130 +1,23 @@
-/*
- * Copyright (C) 2011 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Generated automatically from com.google.gson.TypeAdapter for testing purposes
package com.google.gson;
+import com.google.gson.JsonElement;
import com.google.gson.stream.JsonReader;
-import java.io.IOException;
+import com.google.gson.stream.JsonWriter;
import java.io.Reader;
import java.io.Writer;
-public abstract class TypeAdapter {
- /**
- * Converts {@code value} to a JSON document and writes it to {@code out}.
- * Unlike Gson's similar {@link Gson#toJson(JsonElement, Appendable) toJson}
- * method, this write is strict. Create a {@link
- * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
- * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
- * writing.
- *
- * @param value the Java object to convert. May be null.
- * @since 2.2
- */
- public final void toJson(Writer out, T value) throws IOException {
- }
-
- /**
- * This wrapper method is used to make a type adapter null tolerant. In general, a
- * type adapter is required to handle nulls in write and read methods. Here is how this
- * is typically done:
- *
{@code
- *
- * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
- * new TypeAdapter() {
- * public Foo read(JsonReader in) throws IOException {
- * if (in.peek() == JsonToken.NULL) {
- * in.nextNull();
- * return null;
- * }
- * // read a Foo from in and return it
- * }
- * public void write(JsonWriter out, Foo src) throws IOException {
- * if (src == null) {
- * out.nullValue();
- * return;
- * }
- * // write src as JSON to out
- * }
- * }).create();
- * }
- * You can avoid this boilerplate handling of nulls by wrapping your type adapter with
- * this method. Here is how we will rewrite the above example:
- *
{@code
- *
- * Gson gson = new GsonBuilder().registerTypeAdapter(Foo.class,
- * new TypeAdapter() {
- * public Foo read(JsonReader in) throws IOException {
- * // read a Foo from in and return it
- * }
- * public void write(JsonWriter out, Foo src) throws IOException {
- * // write src as JSON to out
- * }
- * }.nullSafe()).create();
- * }
- * Note that we didn't need to check for nulls in our type adapter after we used nullSafe.
- */
- public final TypeAdapter nullSafe() {
- return null;
- }
-
- /**
- * Converts {@code value} to a JSON document. Unlike Gson's similar {@link
- * Gson#toJson(Object) toJson} method, this write is strict. Create a {@link
- * JsonWriter#setLenient(boolean) lenient} {@code JsonWriter} and call
- * {@link #write(com.google.gson.stream.JsonWriter, Object)} for lenient
- * writing.
- *
- * @param value the Java object to convert. May be null.
- * @since 2.2
- */
- public final String toJson(T value) {
- return null;
- }
-
- /**
- * Reads one JSON value (an array, object, string, number, boolean or null)
- * and converts it to a Java object. Returns the converted object.
- *
- * @return the converted Java object. May be null.
- */
- public abstract T read(JsonReader in) throws IOException;
-
- /**
- * Converts the JSON document in {@code in} to a Java object. Unlike Gson's
- * similar {@link Gson#fromJson(java.io.Reader, Class) fromJson} method, this
- * read is strict. Create a {@link JsonReader#setLenient(boolean) lenient}
- * {@code JsonReader} and call {@link #read(JsonReader)} for lenient reading.
- *
- * @return the converted Java object. May be null.
- * @since 2.2
- */
- public final T fromJson(Reader in) throws IOException {
- return null;
- }
-
- /**
- * Converts the JSON document in {@code json} to a Java object. Unlike Gson's
- * similar {@link Gson#fromJson(String, Class) fromJson} method, this read is
- * strict. Create a {@link JsonReader#setLenient(boolean) lenient} {@code
- * JsonReader} and call {@link #read(JsonReader)} for lenient reading.
- *
- * @return the converted Java object. May be null.
- * @since 2.2
- */
- public final T fromJson(String json) throws IOException {
- return null;
- }
+abstract public class TypeAdapter
+{
+ public TypeAdapter(){}
+ public abstract T read(JsonReader p0);
+ public abstract void write(JsonWriter p0, T p1);
+ public final JsonElement toJsonTree(T p0){ return null; }
+ public final String toJson(T p0){ return null; }
+ public final T fromJson(Reader p0){ return null; }
+ public final T fromJson(String p0){ return null; }
+ public final T fromJsonTree(JsonElement p0){ return null; }
+ public final TypeAdapter nullSafe(){ return null; }
+ public final void toJson(Writer p0, T p1){}
}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java
index d6cc8133712..6b3728f38b0 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/TypeAdapterFactory.java
@@ -1,28 +1,12 @@
-/*
- * Copyright (C) 2011 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Generated automatically from com.google.gson.TypeAdapterFactory for testing purposes
package com.google.gson;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
import com.google.gson.reflect.TypeToken;
-public interface TypeAdapterFactory {
-
- /**
- * Returns a type adapter for {@code type}, or null if this factory doesn't
- * support {@code type}.
- */
- TypeAdapter create(Gson gson, TypeToken type);
-}
\ No newline at end of file
+public interface TypeAdapterFactory
+{
+ com.google.gson.TypeAdapter create(Gson p0, com.google.gson.reflect.TypeToken p1);
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/internal/Excluder.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/internal/Excluder.java
new file mode 100644
index 00000000000..dc05b0477c5
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/internal/Excluder.java
@@ -0,0 +1,25 @@
+// Generated automatically from com.google.gson.internal.Excluder for testing purposes
+
+package com.google.gson.internal;
+
+import com.google.gson.ExclusionStrategy;
+import com.google.gson.Gson;
+import com.google.gson.TypeAdapter;
+import com.google.gson.TypeAdapterFactory;
+import com.google.gson.reflect.TypeToken;
+import java.lang.reflect.Field;
+
+public class Excluder implements Cloneable, TypeAdapterFactory
+{
+ protected Excluder clone(){ return null; }
+ public com.google.gson.TypeAdapter create(Gson p0, com.google.gson.reflect.TypeToken p1){ return null; }
+ public Excluder disableInnerClassSerialization(){ return null; }
+ public Excluder excludeFieldsWithoutExposeAnnotation(){ return null; }
+ public Excluder withExclusionStrategy(ExclusionStrategy p0, boolean p1, boolean p2){ return null; }
+ public Excluder withModifiers(int... p0){ return null; }
+ public Excluder withVersion(double p0){ return null; }
+ public Excluder(){}
+ public boolean excludeClass(Class extends Object> p0, boolean p1){ return false; }
+ public boolean excludeField(Field p0, boolean p1){ return false; }
+ public static Excluder DEFAULT = null;
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java
index a35b2a45b85..ac3b84cb258 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/reflect/TypeToken.java
@@ -1,50 +1,22 @@
-/*
- * Copyright (C) 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
+// Generated automatically from com.google.gson.reflect.TypeToken for testing purposes
package com.google.gson.reflect;
-/**
- * Represents a generic type {@code T}. Java doesn't yet provide a way to
- * represent generic types, so this class does. Forces clients to create a
- * subclass of this class which enables retrieval the type information even at
- * runtime.
- *
- *
For example, to create a type literal for {@code List}, you can
- * create an empty anonymous inner class:
- *
- *
- * {@code TypeToken> list = new TypeToken>() {};}
- *
- *
This syntax cannot be used to create type literals that have wildcard
- * parameters, such as {@code Class>} or {@code List extends CharSequence>}.
- *
- * @author Bob Lee
- * @author Sven Mawson
- * @author Jesse Wilson
- */
-public class TypeToken {
+import java.lang.reflect.Type;
- /**
- * Constructs a new type literal. Derives represented class from type
- * parameter.
- *
- *
Clients create an empty anonymous subclass. Doing so embeds the type
- * parameter in the anonymous class's type hierarchy so we can reconstitute it
- * at runtime despite erasure.
- */
- protected TypeToken() {
- }
-}
\ No newline at end of file
+public class TypeToken
+{
+ protected TypeToken(){}
+ public boolean isAssignableFrom(Class extends Object> p0){ return false; }
+ public boolean isAssignableFrom(Type p0){ return false; }
+ public boolean isAssignableFrom(TypeToken extends Object> p0){ return false; }
+ public final String toString(){ return null; }
+ public final Type getType(){ return null; }
+ public final boolean equals(Object p0){ return false; }
+ public final int hashCode(){ return 0; }
+ public final java.lang.Class super T> getRawType(){ return null; }
+ public static com.google.gson.reflect.TypeToken get(java.lang.Class p0){ return null; }
+ public static TypeToken extends Object> get(Type p0){ return null; }
+ public static TypeToken extends Object> getArray(Type p0){ return null; }
+ public static TypeToken extends Object> getParameterized(Type p0, Type... p1){ return null; }
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java
index 5d0d2ad112f..677d58d8cd8 100644
--- a/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonReader.java
@@ -1,66 +1,33 @@
+// Generated automatically from com.google.gson.stream.JsonReader for testing purposes
+
package com.google.gson.stream;
+import com.google.gson.stream.JsonToken;
import java.io.Closeable;
-import java.io.IOException;
import java.io.Reader;
-public class JsonReader implements Closeable {
- public JsonReader(Reader in) {
- }
-
- public final void setLenient(boolean lenient) {
- }
-
- public final boolean isLenient() {
- return false;
- }
-
- public void beginArray() throws IOException {
- }
-
- public void endArray() throws IOException {
- }
-
- public void beginObject() throws IOException {
- }
-
- public void endObject() throws IOException {
- }
-
- public boolean hasNext() throws IOException {
- return false;
- }
-
- public String nextName() throws IOException {
- return null;
- }
-
- public String nextString() throws IOException {
- return null;
- }
-
- public boolean nextBoolean() throws IOException {
- return false;
- }
-
- public void nextNull() throws IOException {
- }
-
- public double nextDouble() throws IOException {
- return -1;
- }
-
- public long nextLong() throws IOException {
- return -1;
- }
-
- public int nextInt() throws IOException {
- return -1;
- }
-
- public void close() throws IOException {
- }
-
- public void skipValue() throws IOException {
- }
-}
\ No newline at end of file
+public class JsonReader implements Closeable
+{
+ protected JsonReader() {}
+ public JsonReader(Reader p0){}
+ public JsonToken peek(){ return null; }
+ public String getPath(){ return null; }
+ public String getPreviousPath(){ return null; }
+ public String nextName(){ return null; }
+ public String nextString(){ return null; }
+ public String toString(){ return null; }
+ public boolean hasNext(){ return false; }
+ public boolean nextBoolean(){ return false; }
+ public double nextDouble(){ return 0; }
+ public final boolean isLenient(){ return false; }
+ public final void setLenient(boolean p0){}
+ public int nextInt(){ return 0; }
+ public long nextLong(){ return 0; }
+ public void beginArray(){}
+ public void beginObject(){}
+ public void close(){}
+ public void endArray(){}
+ public void endObject(){}
+ public void nextNull(){}
+ public void skipValue(){}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonToken.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonToken.java
new file mode 100644
index 00000000000..fbb2e7ac463
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonToken.java
@@ -0,0 +1,10 @@
+// Generated automatically from com.google.gson.stream.JsonToken for testing purposes
+
+package com.google.gson.stream;
+
+
+public enum JsonToken
+{
+ BEGIN_ARRAY, BEGIN_OBJECT, BOOLEAN, END_ARRAY, END_DOCUMENT, END_OBJECT, NAME, NULL, NUMBER, STRING;
+ private JsonToken() {}
+}
diff --git a/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonWriter.java b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonWriter.java
new file mode 100644
index 00000000000..282343f0bed
--- /dev/null
+++ b/java/ql/test/stubs/gson-2.8.6/com/google/gson/stream/JsonWriter.java
@@ -0,0 +1,36 @@
+// Generated automatically from com.google.gson.stream.JsonWriter for testing purposes
+
+package com.google.gson.stream;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.Writer;
+
+public class JsonWriter implements Closeable, Flushable
+{
+ protected JsonWriter() {}
+ public JsonWriter beginArray(){ return null; }
+ public JsonWriter beginObject(){ return null; }
+ public JsonWriter endArray(){ return null; }
+ public JsonWriter endObject(){ return null; }
+ public JsonWriter jsonValue(String p0){ return null; }
+ public JsonWriter name(String p0){ return null; }
+ public JsonWriter nullValue(){ return null; }
+ public JsonWriter value(Boolean p0){ return null; }
+ public JsonWriter value(Number p0){ return null; }
+ public JsonWriter value(String p0){ return null; }
+ public JsonWriter value(boolean p0){ return null; }
+ public JsonWriter value(double p0){ return null; }
+ public JsonWriter value(float p0){ return null; }
+ public JsonWriter value(long p0){ return null; }
+ public JsonWriter(Writer p0){}
+ public boolean isLenient(){ return false; }
+ public final boolean getSerializeNulls(){ return false; }
+ public final boolean isHtmlSafe(){ return false; }
+ public final void setHtmlSafe(boolean p0){}
+ public final void setIndent(String p0){}
+ public final void setLenient(boolean p0){}
+ public final void setSerializeNulls(boolean p0){}
+ public void close(){}
+ public void flush(){}
+}
From 0151a728f8467d4af670803bb63b34b38fe65069 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 May 2023 17:53:03 +0200
Subject: [PATCH 188/813] Add change note
---
java/ql/lib/change-notes/2023-05-30-gson-models.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/lib/change-notes/2023-05-30-gson-models.md
diff --git a/java/ql/lib/change-notes/2023-05-30-gson-models.md b/java/ql/lib/change-notes/2023-05-30-gson-models.md
new file mode 100644
index 00000000000..306d797ff1a
--- /dev/null
+++ b/java/ql/lib/change-notes/2023-05-30-gson-models.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Added dataflow models for the Gson deserialization library.
From 70138448c3077624c36b4d52e9a04f27200471b6 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Tue, 30 May 2023 17:54:59 +0200
Subject: [PATCH 189/813] Visibility
---
.../code/java/frameworks/google/GsonSerializability.qll | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
index 470847f292e..dba25be7b22 100644
--- a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
@@ -4,9 +4,9 @@
*/
import java
-import semmle.code.java.Serializability
-import semmle.code.java.dataflow.DataFlow
-import semmle.code.java.dataflow.FlowSteps
+private import semmle.code.java.Serializability
+private import semmle.code.java.dataflow.DataFlow
+private import semmle.code.java.dataflow.FlowSteps
/**
* A method used for deserializing objects using Gson. The first parameter is the object to be
@@ -44,7 +44,7 @@ private class FieldReferencedGsonDeserializableType extends GsonDeserializableTy
}
/** A field that may be deserialized using the Gson JSON framework. */
-class GsonDeserializableField extends DeserializableField {
+private class GsonDeserializableField extends DeserializableField {
pragma[assume_small_delta]
GsonDeserializableField() {
exists(GsonDeserializableType superType |
From a8c76388c0005f7ae1a5ab7637ba1b1b74a10667 Mon Sep 17 00:00:00 2001
From: Jeroen Ketema
Date: Tue, 30 May 2023 18:13:22 +0200
Subject: [PATCH 190/813] C++: Fix configuration names in comments in
`cpp/invalid-pointer-deref`
---
.../CWE/CWE-193/InvalidPointerDeref.ql | 22 +++++++++----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
index 07189cea9d9..edfd7a76a5f 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
@@ -81,8 +81,8 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) {
* ```
*
* We do this by splitting the task up into two configurations:
- * 1. `AllocToInvalidPointerConf` find flow from `malloc(size)` to `begin + size`, and
- * 2. `InvalidPointerToDerefConf` finds flow from `begin + size` to an `end` (on line 3).
+ * 1. `AllocToInvalidPointerConfig` find flow from `malloc(size)` to `begin + size`, and
+ * 2. `InvalidPointerToDerefConfig` finds flow from `begin + size` to an `end` (on line 3).
*
* Finally, the range-analysis library will find a load from (or store to) an address that
* is non-strictly upper-bounded by `end` (which in this case is `*p`).
@@ -180,7 +180,7 @@ predicate isSinkImpl(
}
/**
- * Holds if `sink` is a sink for `InvalidPointerToDerefConf` and `i` is a `StoreInstruction` that
+ * Holds if `sink` is a sink for `InvalidPointerToDerefConfig` and `i` is a `StoreInstruction` that
* writes to an address that non-strictly upper-bounds `sink`, or `i` is a `LoadInstruction` that
* reads from an address that non-strictly upper-bounds `sink`.
*/
@@ -201,7 +201,7 @@ predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string o
/**
* A configuration to track flow from a pointer-arithmetic operation found
- * by `AllocToInvalidPointerConf` to a dereference of the pointer.
+ * by `AllocToInvalidPointerConfig` to a dereference of the pointer.
*/
module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, source, _) }
@@ -237,12 +237,12 @@ predicate invalidPointerToDerefSource(
}
newtype TMergedPathNode =
- // The path nodes computed by the first projection of `AllocToInvalidPointerConf`
+ // The path nodes computed by the first projection of `AllocToInvalidPointerConfig`
TPathNode1(AllocToInvalidPointerFlow::PathNode1 p) or
- // The path nodes computed by `InvalidPointerToDerefConf`
+ // The path nodes computed by `InvalidPointerToDerefConfig`
TPathNode3(InvalidPointerToDerefFlow::PathNode p) or
- // The read/write that uses the invalid pointer identified by `InvalidPointerToDerefConf`.
- // This one is needed because the sink identified by `InvalidPointerToDerefConf` is the
+ // The read/write that uses the invalid pointer identified by `InvalidPointerToDerefConfig`.
+ // This one is needed because the sink identified by `InvalidPointerToDerefConfig` is the
// pointer, but we want to raise an alert at the dereference.
TPathNodeSink(Instruction i) {
exists(DataFlow::Node n |
@@ -335,8 +335,8 @@ query predicate subpaths(
}
/**
- * Holds if `p1` is a sink of `AllocToInvalidPointerConf` and `p2` is a source
- * of `InvalidPointerToDerefConf`, and they are connected through `pai`.
+ * Holds if `p1` is a sink of `AllocToInvalidPointerConfig` and `p2` is a source
+ * of `InvalidPointerToDerefConfig`, and they are connected through `pai`.
*/
predicate joinOn1(
PointerArithmeticInstruction pai, AllocToInvalidPointerFlow::PathNode1 p1,
@@ -347,7 +347,7 @@ predicate joinOn1(
}
/**
- * Holds if `p1` is a sink of `InvalidPointerToDerefConf` and `i` is the instruction
+ * Holds if `p1` is a sink of `InvalidPointerToDerefConfig` and `i` is the instruction
* that dereferences `p1`. The string `operation` describes whether the `i` is
* a `StoreInstruction` or `LoadInstruction`.
*/
From de974cc18a4792e7329de3f4323f42e4485048b0 Mon Sep 17 00:00:00 2001
From: Jeroen Ketema
Date: Tue, 30 May 2023 18:15:59 +0200
Subject: [PATCH 191/813] C++: Add `cpp/invalid-pointer-deref` test case that
shows some duplicate results
---
.../InvalidPointerDeref.expected | 57 +++++++++++++++++++
.../CWE/CWE-193/pointer-deref/test.cpp | 8 +++
2 files changed, 65 insertions(+)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index 1487088ca9f..4c6693a9d20 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -671,6 +671,58 @@ edges
| test.cpp:350:16:350:19 | ... ++ | test.cpp:350:15:350:19 | Load: * ... |
| test.cpp:350:16:350:19 | ... ++ | test.cpp:350:16:350:19 | ... ++ |
| test.cpp:350:16:350:19 | ... ++ | test.cpp:350:16:350:19 | ... ++ |
+| test.cpp:355:14:355:27 | new[] | test.cpp:356:15:356:16 | xs |
+| test.cpp:356:15:356:16 | xs | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:357:24:357:26 | end |
+| test.cpp:356:15:356:16 | xs | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:356:15:356:16 | xs | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:356:15:356:16 | xs | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:356:15:356:16 | xs | test.cpp:359:16:359:27 | end_plus_one |
+| test.cpp:356:15:356:16 | xs | test.cpp:359:16:359:31 | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:356:15:356:23 | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:357:24:357:26 | end |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:357:24:357:26 | end |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:356:15:356:23 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:26 | end | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:357:24:357:26 | end | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:357:24:357:30 | ... + ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:358:15:358:26 | end_plus_one |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:16:359:27 | end_plus_one |
+| test.cpp:357:24:357:30 | ... + ... | test.cpp:359:16:359:27 | end_plus_one |
+| test.cpp:358:15:358:26 | end_plus_one | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:358:15:358:26 | end_plus_one | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:358:15:358:26 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:358:15:358:26 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:358:15:358:26 | end_plus_one | test.cpp:359:16:359:27 | end_plus_one |
+| test.cpp:359:16:359:27 | end_plus_one | test.cpp:358:14:358:26 | Load: * ... |
+| test.cpp:359:16:359:27 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:359:16:359:31 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
subpaths
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
@@ -699,3 +751,8 @@ subpaths
| test.cpp:333:5:333:21 | Store: ... = ... | test.cpp:325:14:325:27 | new[] | test.cpp:333:5:333:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:325:14:325:27 | new[] | new[] | test.cpp:326:20:326:23 | size | size |
| test.cpp:341:5:341:21 | Store: ... = ... | test.cpp:325:14:325:27 | new[] | test.cpp:341:5:341:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:325:14:325:27 | new[] | new[] | test.cpp:326:20:326:23 | size | size |
| test.cpp:350:15:350:19 | Load: * ... | test.cpp:347:14:347:27 | new[] | test.cpp:350:15:350:19 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:347:14:347:27 | new[] | new[] | test.cpp:348:20:348:23 | size | size |
+| test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
+| test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
+| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
+| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
+| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
index b6741535e42..3dfd8b89097 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
@@ -350,3 +350,11 @@ void test24(unsigned size) {
int val = *xs++; // GOOD [FALSE POSITIVE]
}
}
+
+void test25(unsigned size) {
+ char *xs = new char[size];
+ char *end = xs + size;
+ char *end_plus_one = end + 1;
+ int val1 = *end_plus_one; // BAD
+ int val2 = *(end_plus_one + 1); // BAD
+}
From f5ed02a43376a503b3efd2ce4aadf97520c628aa Mon Sep 17 00:00:00 2001
From: Jeroen Ketema
Date: Tue, 30 May 2023 18:23:22 +0200
Subject: [PATCH 192/813] C++: Take into account the delta at the final sink in
`cpp/invalid-pointer-deref`
---
.../CWE/CWE-193/InvalidPointerDeref.ql | 26 +++++++++----------
.../InvalidPointerDeref.expected | 6 -----
2 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
index edfd7a76a5f..646843d077c 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
@@ -185,8 +185,8 @@ predicate isSinkImpl(
* reads from an address that non-strictly upper-bounds `sink`.
*/
pragma[inline]
-predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation) {
- exists(AddressOperand addr, int delta |
+predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation, int delta) {
+ exists(AddressOperand addr |
bounded1(addr.getDef(), sink.asInstruction(), delta) and
delta >= 0 and
i.getAnOperand() = addr
@@ -207,7 +207,7 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { invalidPointerToDerefSource(_, source, _) }
pragma[inline]
- predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _) }
+ predicate isSink(DataFlow::Node sink) { isInvalidPointerDerefSink(sink, _, _, _) }
predicate isBarrier(DataFlow::Node node) {
node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true)
@@ -247,7 +247,7 @@ newtype TMergedPathNode =
TPathNodeSink(Instruction i) {
exists(DataFlow::Node n |
InvalidPointerToDerefFlow::flowTo(n) and
- isInvalidPointerDerefSink(n, i, _)
+ isInvalidPointerDerefSink(n, i, _, _)
)
}
@@ -321,7 +321,7 @@ query predicate edges(MergedPathNode node1, MergedPathNode node2) {
or
node1.asPathNode3().getASuccessor() = node2.asPathNode3()
or
- joinOn2(node1.asPathNode3(), node2.asSinkNode(), _)
+ joinOn2(node1.asPathNode3(), node2.asSinkNode(), _, _)
}
query predicate subpaths(
@@ -352,32 +352,32 @@ predicate joinOn1(
* a `StoreInstruction` or `LoadInstruction`.
*/
pragma[inline]
-predicate joinOn2(InvalidPointerToDerefFlow::PathNode p1, Instruction i, string operation) {
- isInvalidPointerDerefSink(p1.getNode(), i, operation)
+predicate joinOn2(InvalidPointerToDerefFlow::PathNode p1, Instruction i, string operation, int delta) {
+ isInvalidPointerDerefSink(p1.getNode(), i, operation, delta)
}
predicate hasFlowPath(
MergedPathNode source1, MergedPathNode sink, InvalidPointerToDerefFlow::PathNode source3,
- PointerArithmeticInstruction pai, string operation
+ PointerArithmeticInstruction pai, string operation, int delta
) {
exists(InvalidPointerToDerefFlow::PathNode sink3, AllocToInvalidPointerFlow::PathNode1 sink1 |
AllocToInvalidPointerFlow::flowPath(source1.asPathNode1(), _, sink1, _) and
joinOn1(pai, sink1, source3) and
InvalidPointerToDerefFlow::flowPath(source3, sink3) and
- joinOn2(sink3, sink.asSinkNode(), operation)
+ joinOn2(sink3, sink.asSinkNode(), operation, delta)
)
}
from
- MergedPathNode source, MergedPathNode sink, int k, string kstr,
+ MergedPathNode source, MergedPathNode sink, int k2, int k3, string kstr,
InvalidPointerToDerefFlow::PathNode source3, PointerArithmeticInstruction pai, string operation,
Expr offset, DataFlow::Node n
where
- hasFlowPath(source, sink, source3, pai, operation) and
- invalidPointerToDerefSource(pai, source3.getNode(), k) and
+ hasFlowPath(source, sink, source3, pai, operation, k3) and
+ invalidPointerToDerefSource(pai, source3.getNode(), k2) and
offset = pai.getRight().getUnconvertedResultExpression() and
n = source.asPathNode1().getNode() and
- if k = 0 then kstr = "" else kstr = " + " + k
+ if (k2 + k3) = 0 then kstr = "" else kstr = " + " + (k2 + k3)
select sink, source, sink,
"This " + operation + " might be out of bounds, as the pointer might be equal to $@ + $@" + kstr +
".", n, n.toString(), offset, offset.toString()
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index 4c6693a9d20..ba5363dc4fa 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -727,14 +727,11 @@ subpaths
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
-| test.cpp:8:14:8:21 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:8:14:8:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
| test.cpp:20:14:20:21 | Load: * ... | test.cpp:16:15:16:20 | call to malloc | test.cpp:20:14:20:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:16:15:16:20 | call to malloc | call to malloc | test.cpp:17:19:17:22 | size | size |
| test.cpp:30:14:30:15 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:30:14:30:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
| test.cpp:32:14:32:21 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:32:14:32:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
-| test.cpp:32:14:32:21 | Load: * ... | test.cpp:28:15:28:20 | call to malloc | test.cpp:32:14:32:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:28:15:28:20 | call to malloc | call to malloc | test.cpp:29:20:29:27 | ... + ... | ... + ... |
| test.cpp:42:14:42:15 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:42:14:42:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
-| test.cpp:44:14:44:21 | Load: * ... | test.cpp:40:15:40:20 | call to malloc | test.cpp:44:14:44:21 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:40:15:40:20 | call to malloc | call to malloc | test.cpp:41:20:41:27 | ... - ... | ... - ... |
| test.cpp:67:9:67:14 | Store: ... = ... | test.cpp:52:19:52:24 | call to malloc | test.cpp:67:9:67:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:52:19:52:24 | call to malloc | call to malloc | test.cpp:53:20:53:23 | size | size |
| test.cpp:96:9:96:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:96:9:96:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
| test.cpp:110:9:110:14 | Store: ... = ... | test.cpp:82:17:82:22 | call to malloc | test.cpp:110:9:110:14 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:82:17:82:22 | call to malloc | call to malloc | test.cpp:83:27:83:30 | size | size |
@@ -752,7 +749,4 @@ subpaths
| test.cpp:341:5:341:21 | Store: ... = ... | test.cpp:325:14:325:27 | new[] | test.cpp:341:5:341:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:325:14:325:27 | new[] | new[] | test.cpp:326:20:326:23 | size | size |
| test.cpp:350:15:350:19 | Load: * ... | test.cpp:347:14:347:27 | new[] | test.cpp:350:15:350:19 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:347:14:347:27 | new[] | new[] | test.cpp:348:20:348:23 | size | size |
| test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
-| test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
-| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
-| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
From dd30acf1e335445613f674ba855557fac25287fb Mon Sep 17 00:00:00 2001
From: Jeroen Ketema
Date: Tue, 30 May 2023 18:43:01 +0200
Subject: [PATCH 193/813] C++: Add nodes query predicate to
`cpp/invalid-pointer-deref`
---
.../CWE/CWE-193/InvalidPointerDeref.ql | 8 +
.../InvalidPointerDeref.expected | 327 ++++++++++++++++++
2 files changed, 335 insertions(+)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
index 646843d077c..610eb572d8c 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql
@@ -324,6 +324,14 @@ query predicate edges(MergedPathNode node1, MergedPathNode node2) {
joinOn2(node1.asPathNode3(), node2.asSinkNode(), _, _)
}
+query predicate nodes(MergedPathNode n, string key, string val) {
+ AllocToInvalidPointerFlow::PathGraph1::nodes(n.asPathNode1(), key, val)
+ or
+ InvalidPointerToDerefFlow::PathGraph::nodes(n.asPathNode3(), key, val)
+ or
+ key = "semmle.label" and val = n.asSinkNode().toString()
+}
+
query predicate subpaths(
MergedPathNode arg, MergedPathNode par, MergedPathNode ret, MergedPathNode out
) {
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index ba5363dc4fa..6b4d039ee6b 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -723,6 +723,333 @@ edges
| test.cpp:359:16:359:27 | end_plus_one | test.cpp:358:14:358:26 | Load: * ... |
| test.cpp:359:16:359:27 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... |
| test.cpp:359:16:359:31 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+nodes
+| test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:5:15:5:15 | p | semmle.label | p |
+| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
+| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
+| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
+| test.cpp:5:15:5:22 | ... + ... | semmle.label | ... + ... |
+| test.cpp:6:14:6:15 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:6:15:6:15 | q | semmle.label | q |
+| test.cpp:6:15:6:15 | q | semmle.label | q |
+| test.cpp:7:16:7:16 | q | semmle.label | q |
+| test.cpp:7:16:7:16 | q | semmle.label | q |
+| test.cpp:8:14:8:21 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:8:16:8:16 | q | semmle.label | q |
+| test.cpp:8:16:8:16 | q | semmle.label | q |
+| test.cpp:8:16:8:20 | ... + ... | semmle.label | ... + ... |
+| test.cpp:9:16:9:16 | q | semmle.label | q |
+| test.cpp:9:16:9:16 | q | semmle.label | q |
+| test.cpp:10:16:10:16 | q | semmle.label | q |
+| test.cpp:10:16:10:16 | q | semmle.label | q |
+| test.cpp:11:16:11:16 | q | semmle.label | q |
+| test.cpp:11:16:11:16 | q | semmle.label | q |
+| test.cpp:12:16:12:16 | q | semmle.label | q |
+| test.cpp:16:15:16:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:17:15:17:15 | p | semmle.label | p |
+| test.cpp:17:15:17:22 | ... + ... | semmle.label | ... + ... |
+| test.cpp:20:14:20:21 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:20:16:20:20 | ... + ... | semmle.label | ... + ... |
+| test.cpp:28:15:28:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:29:15:29:15 | p | semmle.label | p |
+| test.cpp:29:15:29:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:29:15:29:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:29:15:29:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:29:15:29:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:30:14:30:15 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:30:15:30:15 | q | semmle.label | q |
+| test.cpp:30:15:30:15 | q | semmle.label | q |
+| test.cpp:31:16:31:16 | q | semmle.label | q |
+| test.cpp:31:16:31:16 | q | semmle.label | q |
+| test.cpp:32:14:32:21 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:32:16:32:16 | q | semmle.label | q |
+| test.cpp:32:16:32:16 | q | semmle.label | q |
+| test.cpp:32:16:32:20 | ... + ... | semmle.label | ... + ... |
+| test.cpp:33:16:33:16 | q | semmle.label | q |
+| test.cpp:33:16:33:16 | q | semmle.label | q |
+| test.cpp:34:16:34:16 | q | semmle.label | q |
+| test.cpp:34:16:34:16 | q | semmle.label | q |
+| test.cpp:35:16:35:16 | q | semmle.label | q |
+| test.cpp:35:16:35:16 | q | semmle.label | q |
+| test.cpp:36:16:36:16 | q | semmle.label | q |
+| test.cpp:40:15:40:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:41:15:41:15 | p | semmle.label | p |
+| test.cpp:41:15:41:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:41:15:41:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:41:15:41:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:41:15:41:28 | ... + ... | semmle.label | ... + ... |
+| test.cpp:42:14:42:15 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:42:15:42:15 | q | semmle.label | q |
+| test.cpp:42:15:42:15 | q | semmle.label | q |
+| test.cpp:43:16:43:16 | q | semmle.label | q |
+| test.cpp:43:16:43:16 | q | semmle.label | q |
+| test.cpp:44:14:44:21 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:44:16:44:16 | q | semmle.label | q |
+| test.cpp:44:16:44:16 | q | semmle.label | q |
+| test.cpp:44:16:44:20 | ... + ... | semmle.label | ... + ... |
+| test.cpp:45:16:45:16 | q | semmle.label | q |
+| test.cpp:45:16:45:16 | q | semmle.label | q |
+| test.cpp:46:16:46:16 | q | semmle.label | q |
+| test.cpp:46:16:46:16 | q | semmle.label | q |
+| test.cpp:47:16:47:16 | q | semmle.label | q |
+| test.cpp:47:16:47:16 | q | semmle.label | q |
+| test.cpp:48:16:48:16 | q | semmle.label | q |
+| test.cpp:51:7:51:14 | mk_array indirection | semmle.label | mk_array indirection |
+| test.cpp:51:33:51:35 | end | semmle.label | end |
+| test.cpp:52:19:52:24 | call to malloc | semmle.label | call to malloc |
+| test.cpp:53:5:53:23 | ... = ... | semmle.label | ... = ... |
+| test.cpp:53:12:53:16 | begin | semmle.label | begin |
+| test.cpp:53:12:53:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:60:19:60:26 | call to mk_array | semmle.label | call to mk_array |
+| test.cpp:60:34:60:37 | mk_array output argument | semmle.label | mk_array output argument |
+| test.cpp:62:32:62:34 | end | semmle.label | end |
+| test.cpp:62:39:62:39 | p | semmle.label | p |
+| test.cpp:66:32:66:34 | end | semmle.label | end |
+| test.cpp:66:39:66:39 | p | semmle.label | p |
+| test.cpp:67:9:67:14 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:70:31:70:33 | end | semmle.label | end |
+| test.cpp:70:38:70:38 | p | semmle.label | p |
+| test.cpp:80:9:80:16 | mk_array indirection [begin] | semmle.label | mk_array indirection [begin] |
+| test.cpp:80:9:80:16 | mk_array indirection [end] | semmle.label | mk_array indirection [end] |
+| test.cpp:82:5:82:28 | ... = ... | semmle.label | ... = ... |
+| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] |
+| test.cpp:82:17:82:22 | call to malloc | semmle.label | call to malloc |
+| test.cpp:83:5:83:30 | ... = ... | semmle.label | ... = ... |
+| test.cpp:83:9:83:11 | arr indirection [post update] [end] | semmle.label | arr indirection [post update] [end] |
+| test.cpp:83:15:83:17 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:83:15:83:30 | ... + ... | semmle.label | ... + ... |
+| test.cpp:83:19:83:23 | begin | semmle.label | begin |
+| test.cpp:83:19:83:23 | begin indirection | semmle.label | begin indirection |
+| test.cpp:89:19:89:26 | call to mk_array [begin] | semmle.label | call to mk_array [begin] |
+| test.cpp:89:19:89:26 | call to mk_array [end] | semmle.label | call to mk_array [end] |
+| test.cpp:91:20:91:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:91:24:91:28 | begin | semmle.label | begin |
+| test.cpp:91:24:91:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:91:36:91:38 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:91:40:91:42 | end | semmle.label | end |
+| test.cpp:91:40:91:42 | end indirection | semmle.label | end indirection |
+| test.cpp:91:47:91:47 | p | semmle.label | p |
+| test.cpp:95:20:95:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:95:24:95:28 | begin | semmle.label | begin |
+| test.cpp:95:24:95:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:95:36:95:38 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:95:40:95:42 | end | semmle.label | end |
+| test.cpp:95:40:95:42 | end indirection | semmle.label | end indirection |
+| test.cpp:95:47:95:47 | p | semmle.label | p |
+| test.cpp:96:9:96:14 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:99:20:99:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:99:24:99:28 | begin | semmle.label | begin |
+| test.cpp:99:24:99:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:99:35:99:37 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:99:39:99:41 | end | semmle.label | end |
+| test.cpp:99:39:99:41 | end indirection | semmle.label | end indirection |
+| test.cpp:99:46:99:46 | p | semmle.label | p |
+| test.cpp:104:27:104:29 | arr [begin] | semmle.label | arr [begin] |
+| test.cpp:104:27:104:29 | arr [end] | semmle.label | arr [end] |
+| test.cpp:105:20:105:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:105:24:105:28 | begin | semmle.label | begin |
+| test.cpp:105:24:105:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:105:36:105:38 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:105:40:105:42 | end | semmle.label | end |
+| test.cpp:105:40:105:42 | end indirection | semmle.label | end indirection |
+| test.cpp:105:47:105:47 | p | semmle.label | p |
+| test.cpp:109:20:109:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:109:24:109:28 | begin | semmle.label | begin |
+| test.cpp:109:24:109:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:109:36:109:38 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:109:40:109:42 | end | semmle.label | end |
+| test.cpp:109:40:109:42 | end indirection | semmle.label | end indirection |
+| test.cpp:109:47:109:47 | p | semmle.label | p |
+| test.cpp:110:9:110:14 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:113:20:113:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:113:24:113:28 | begin | semmle.label | begin |
+| test.cpp:113:24:113:28 | begin indirection | semmle.label | begin indirection |
+| test.cpp:113:35:113:37 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:113:39:113:41 | end | semmle.label | end |
+| test.cpp:113:39:113:41 | end indirection | semmle.label | end indirection |
+| test.cpp:113:46:113:46 | p | semmle.label | p |
+| test.cpp:119:18:119:25 | call to mk_array [begin] | semmle.label | call to mk_array [begin] |
+| test.cpp:119:18:119:25 | call to mk_array [end] | semmle.label | call to mk_array [end] |
+| test.cpp:124:15:124:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:125:5:125:17 | ... = ... | semmle.label | ... = ... |
+| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] |
+| test.cpp:126:15:126:15 | p | semmle.label | p |
+| test.cpp:129:11:129:13 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:129:15:129:19 | begin | semmle.label | begin |
+| test.cpp:129:15:129:19 | begin indirection | semmle.label | begin indirection |
+| test.cpp:133:11:133:13 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:133:15:133:19 | begin | semmle.label | begin |
+| test.cpp:133:15:133:19 | begin indirection | semmle.label | begin indirection |
+| test.cpp:137:11:137:13 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:137:15:137:19 | begin | semmle.label | begin |
+| test.cpp:137:15:137:19 | begin indirection | semmle.label | begin indirection |
+| test.cpp:141:10:141:19 | mk_array_p indirection [begin] | semmle.label | mk_array_p indirection [begin] |
+| test.cpp:141:10:141:19 | mk_array_p indirection [end] | semmle.label | mk_array_p indirection [end] |
+| test.cpp:143:5:143:29 | ... = ... | semmle.label | ... = ... |
+| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | semmle.label | arr indirection [post update] [begin] |
+| test.cpp:143:18:143:23 | call to malloc | semmle.label | call to malloc |
+| test.cpp:144:5:144:32 | ... = ... | semmle.label | ... = ... |
+| test.cpp:144:10:144:12 | arr indirection [post update] [end] | semmle.label | arr indirection [post update] [end] |
+| test.cpp:144:16:144:18 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:144:16:144:32 | ... + ... | semmle.label | ... + ... |
+| test.cpp:144:21:144:25 | begin | semmle.label | begin |
+| test.cpp:144:21:144:25 | begin indirection | semmle.label | begin indirection |
+| test.cpp:150:20:150:29 | call to mk_array_p indirection [begin] | semmle.label | call to mk_array_p indirection [begin] |
+| test.cpp:150:20:150:29 | call to mk_array_p indirection [end] | semmle.label | call to mk_array_p indirection [end] |
+| test.cpp:152:20:152:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:152:25:152:29 | begin | semmle.label | begin |
+| test.cpp:152:25:152:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:152:49:152:49 | p | semmle.label | p |
+| test.cpp:156:20:156:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:156:25:156:29 | begin | semmle.label | begin |
+| test.cpp:156:25:156:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:156:37:156:39 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:156:42:156:44 | end | semmle.label | end |
+| test.cpp:156:42:156:44 | end indirection | semmle.label | end indirection |
+| test.cpp:156:49:156:49 | p | semmle.label | p |
+| test.cpp:157:9:157:14 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:160:20:160:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:160:25:160:29 | begin | semmle.label | begin |
+| test.cpp:160:25:160:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:160:48:160:48 | p | semmle.label | p |
+| test.cpp:165:29:165:31 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:165:29:165:31 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:166:20:166:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:166:25:166:29 | begin | semmle.label | begin |
+| test.cpp:166:25:166:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:166:37:166:39 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:166:42:166:44 | end | semmle.label | end |
+| test.cpp:166:42:166:44 | end indirection | semmle.label | end indirection |
+| test.cpp:166:49:166:49 | p | semmle.label | p |
+| test.cpp:170:20:170:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:170:25:170:29 | begin | semmle.label | begin |
+| test.cpp:170:25:170:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:170:37:170:39 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:170:42:170:44 | end | semmle.label | end |
+| test.cpp:170:42:170:44 | end indirection | semmle.label | end indirection |
+| test.cpp:170:49:170:49 | p | semmle.label | p |
+| test.cpp:171:9:171:14 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:174:20:174:22 | arr indirection [begin] | semmle.label | arr indirection [begin] |
+| test.cpp:174:25:174:29 | begin | semmle.label | begin |
+| test.cpp:174:25:174:29 | begin indirection | semmle.label | begin indirection |
+| test.cpp:174:36:174:38 | arr indirection [end] | semmle.label | arr indirection [end] |
+| test.cpp:174:41:174:43 | end | semmle.label | end |
+| test.cpp:174:41:174:43 | end indirection | semmle.label | end indirection |
+| test.cpp:174:48:174:48 | p | semmle.label | p |
+| test.cpp:180:19:180:28 | call to mk_array_p indirection [begin] | semmle.label | call to mk_array_p indirection [begin] |
+| test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | semmle.label | call to mk_array_p indirection [end] |
+| test.cpp:188:15:188:20 | call to malloc | semmle.label | call to malloc |
+| test.cpp:189:15:189:15 | p | semmle.label | p |
+| test.cpp:194:23:194:28 | call to malloc | semmle.label | call to malloc |
+| test.cpp:195:17:195:17 | p | semmle.label | p |
+| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:195:17:195:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:197:8:197:8 | p | semmle.label | p |
+| test.cpp:197:20:197:22 | end | semmle.label | end |
+| test.cpp:201:5:201:5 | p | semmle.label | p |
+| test.cpp:201:5:201:12 | access to array | semmle.label | access to array |
+| test.cpp:201:5:201:19 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:205:23:205:28 | call to malloc | semmle.label | call to malloc |
+| test.cpp:206:17:206:17 | p | semmle.label | p |
+| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:206:17:206:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:208:15:208:15 | p | semmle.label | p |
+| test.cpp:209:12:209:14 | end | semmle.label | end |
+| test.cpp:213:5:213:6 | * ... | semmle.label | * ... |
+| test.cpp:213:5:213:13 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:213:6:213:6 | q | semmle.label | q |
+| test.cpp:213:6:213:6 | q | semmle.label | q |
+| test.cpp:221:17:221:22 | call to malloc | semmle.label | call to malloc |
+| test.cpp:222:5:222:5 | p | semmle.label | p |
+| test.cpp:231:18:231:30 | new[] | semmle.label | new[] |
+| test.cpp:232:3:232:9 | newname | semmle.label | newname |
+| test.cpp:232:3:232:16 | access to array | semmle.label | access to array |
+| test.cpp:232:3:232:20 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:238:20:238:32 | new[] | semmle.label | new[] |
+| test.cpp:239:5:239:11 | newname | semmle.label | newname |
+| test.cpp:239:5:239:18 | access to array | semmle.label | access to array |
+| test.cpp:239:5:239:22 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:248:24:248:30 | call to realloc | semmle.label | call to realloc |
+| test.cpp:249:9:249:9 | p | semmle.label | p |
+| test.cpp:250:22:250:22 | p | semmle.label | p |
+| test.cpp:254:9:254:9 | p | semmle.label | p |
+| test.cpp:254:9:254:12 | access to array | semmle.label | access to array |
+| test.cpp:254:9:254:16 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:260:13:260:24 | new[] | semmle.label | new[] |
+| test.cpp:261:14:261:15 | xs | semmle.label | xs |
+| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:261:14:261:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:262:26:262:28 | end | semmle.label | end |
+| test.cpp:262:26:262:28 | end | semmle.label | end |
+| test.cpp:262:31:262:31 | x | semmle.label | x |
+| test.cpp:264:13:264:14 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:264:14:264:14 | x | semmle.label | x |
+| test.cpp:264:14:264:14 | x | semmle.label | x |
+| test.cpp:270:13:270:24 | new[] | semmle.label | new[] |
+| test.cpp:271:14:271:15 | xs | semmle.label | xs |
+| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:271:14:271:21 | ... + ... | semmle.label | ... + ... |
+| test.cpp:272:26:272:28 | end | semmle.label | end |
+| test.cpp:272:26:272:28 | end | semmle.label | end |
+| test.cpp:272:31:272:31 | x | semmle.label | x |
+| test.cpp:272:31:272:31 | x | semmle.label | x |
+| test.cpp:274:5:274:6 | * ... | semmle.label | * ... |
+| test.cpp:274:5:274:10 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:274:6:274:6 | x | semmle.label | x |
+| test.cpp:274:6:274:6 | x | semmle.label | x |
+| test.cpp:280:13:280:24 | new[] | semmle.label | new[] |
+| test.cpp:281:14:281:15 | xs | semmle.label | xs |
+| test.cpp:290:13:290:24 | new[] | semmle.label | new[] |
+| test.cpp:291:14:291:15 | xs | semmle.label | xs |
+| test.cpp:292:30:292:30 | x | semmle.label | x |
+| test.cpp:304:15:304:26 | new[] | semmle.label | new[] |
+| test.cpp:307:5:307:6 | xs | semmle.label | xs |
+| test.cpp:308:5:308:6 | xs | semmle.label | xs |
+| test.cpp:308:5:308:11 | access to array | semmle.label | access to array |
+| test.cpp:308:5:308:29 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:313:14:313:27 | new[] | semmle.label | new[] |
+| test.cpp:314:15:314:16 | xs | semmle.label | xs |
+| test.cpp:325:14:325:27 | new[] | semmle.label | new[] |
+| test.cpp:326:15:326:16 | xs | semmle.label | xs |
+| test.cpp:326:15:326:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:326:15:326:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:333:5:333:21 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:338:8:338:15 | * ... | semmle.label | * ... |
+| test.cpp:341:5:341:21 | Store: ... = ... | semmle.label | Store: ... = ... |
+| test.cpp:341:8:341:17 | * ... | semmle.label | * ... |
+| test.cpp:342:8:342:17 | * ... | semmle.label | * ... |
+| test.cpp:347:14:347:27 | new[] | semmle.label | new[] |
+| test.cpp:348:15:348:16 | xs | semmle.label | xs |
+| test.cpp:350:15:350:19 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:350:16:350:19 | ... ++ | semmle.label | ... ++ |
+| test.cpp:350:16:350:19 | ... ++ | semmle.label | ... ++ |
+| test.cpp:350:16:350:19 | ... ++ | semmle.label | ... ++ |
+| test.cpp:355:14:355:27 | new[] | semmle.label | new[] |
+| test.cpp:356:15:356:16 | xs | semmle.label | xs |
+| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:356:15:356:23 | ... + ... | semmle.label | ... + ... |
+| test.cpp:357:24:357:26 | end | semmle.label | end |
+| test.cpp:357:24:357:30 | ... + ... | semmle.label | ... + ... |
+| test.cpp:357:24:357:30 | ... + ... | semmle.label | ... + ... |
+| test.cpp:357:24:357:30 | ... + ... | semmle.label | ... + ... |
+| test.cpp:357:24:357:30 | ... + ... | semmle.label | ... + ... |
+| test.cpp:358:14:358:26 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:358:15:358:26 | end_plus_one | semmle.label | end_plus_one |
+| test.cpp:358:15:358:26 | end_plus_one | semmle.label | end_plus_one |
+| test.cpp:359:14:359:32 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:359:16:359:27 | end_plus_one | semmle.label | end_plus_one |
+| test.cpp:359:16:359:31 | ... + ... | semmle.label | ... + ... |
subpaths
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
From 75f6355bd6950cff005897612d6b9ff518d0ba6b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 31 May 2023 04:06:22 +0000
Subject: [PATCH 194/813] Bump chrono from 0.4.25 to 0.4.26 in /ql
Bumps [chrono](https://github.com/chronotope/chrono) from 0.4.25 to 0.4.26.
- [Release notes](https://github.com/chronotope/chrono/releases)
- [Changelog](https://github.com/chronotope/chrono/blob/main/CHANGELOG.md)
- [Commits](https://github.com/chronotope/chrono/compare/v0.4.25...v0.4.26)
---
updated-dependencies:
- dependency-name: chrono
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
---
ql/Cargo.lock | Bin 31667 -> 31667 bytes
ql/buramu/Cargo.toml | 2 +-
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/ql/Cargo.lock b/ql/Cargo.lock
index 76437a85e057d43e2bf6bd6efa08833e2d6e19f3..de833d37b96bcb4b4ef42188f3f137daa116c0b8 100644
GIT binary patch
delta 84
zcmdn|opJMb#tpx@Co4My*rq017@H@W8ycIMCK*|znVFiJr6i@8npq|pnL--nIxO0rC3fDl$Y4NfJZnV0Gh5DegFUf
delta 87
zcmWNJQ3~T>0g1>VhK>rasO6c4ut2wHTDx
o{<8Q4^*J&Xk`%$w78KIV$=m@^7q!u00&~t@Fw(gXeZ4|BK3NPMyZ`_I
diff --git a/ql/buramu/Cargo.toml b/ql/buramu/Cargo.toml
index a84be37dbd7..13aaddaf989 100644
--- a/ql/buramu/Cargo.toml
+++ b/ql/buramu/Cargo.toml
@@ -7,6 +7,6 @@ edition = "2018"
[dependencies]
lazy_static = "1.4.0"
-chrono = "0.4.25"
+chrono = "0.4.26"
rayon = "1.7.0"
regex = "1.8.3"
From b343dcaadd01e19b610269b2bbbb1e709e509f57 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Wed, 31 May 2023 08:06:04 +0200
Subject: [PATCH 195/813] put string/object in the alert-message for
sql-injection
---
.../ql/src/Security/CWE-089/SqlInjection.ql | 9 +-
.../ql/src/Security/CWE-089/SqlInjection.ql | 12 +-
.../CWE-089/typed/SqlInjection.expected | 6 +-
.../CWE-089/untyped/SqlInjection.expected | 250 +++++++++---------
4 files changed, 139 insertions(+), 138 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.ql b/javascript/ql/src/Security/CWE-089/SqlInjection.ql
index 6b0502f611f..f7a40bb91f9 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.ql
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.ql
@@ -18,12 +18,13 @@ import semmle.javascript.security.dataflow.SqlInjectionQuery as SqlInjection
import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
import DataFlow::PathGraph
-from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string type
where
(
- cfg instanceof SqlInjection::Configuration or
- cfg instanceof NosqlInjection::Configuration
+ cfg instanceof SqlInjection::Configuration and type = "string"
+ or
+ cfg instanceof NosqlInjection::Configuration and type = "object"
) and
cfg.hasFlowPath(source, sink)
-select sink.getNode(), source, sink, "This query depends on a $@.", source.getNode(),
+select sink.getNode(), source, sink, "This query " + type + " depends on a $@.", source.getNode(),
"user-provided value"
diff --git a/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-089/SqlInjection.ql b/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-089/SqlInjection.ql
index a2c4a4158bc..e82b9d40d5b 100644
--- a/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-089/SqlInjection.ql
+++ b/javascript/ql/src/experimental/heuristics/ql/src/Security/CWE-089/SqlInjection.ql
@@ -20,13 +20,13 @@ import semmle.javascript.security.dataflow.NosqlInjectionQuery as NosqlInjection
import DataFlow::PathGraph
import semmle.javascript.heuristics.AdditionalSources
-from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
+from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink, string type
where
(
- cfg instanceof SqlInjection::Configuration or
- cfg instanceof NosqlInjection::Configuration
+ cfg instanceof SqlInjection::Configuration and type = "string"
+ or
+ cfg instanceof NosqlInjection::Configuration and type = "object"
) and
- cfg.hasFlowPath(source, sink) and
- source.getNode() instanceof HeuristicSource
-select sink.getNode(), source, sink, "This query depends on a $@.", source.getNode(),
+ cfg.hasFlowPath(source, sink)
+select sink.getNode(), source, sink, "This query " + type + " depends on a $@.", source.getNode(),
"user-provided value"
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/typed/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/typed/SqlInjection.expected
index f6a8d8862f6..acf7e712ee2 100644
--- a/javascript/ql/test/query-tests/Security/CWE-089/typed/SqlInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-089/typed/SqlInjection.expected
@@ -37,6 +37,6 @@ edges
| typedClient.ts:23:33:23:33 | v | typedClient.ts:23:27:23:35 | { id: v } |
| typedClient.ts:23:33:23:33 | v | typedClient.ts:23:27:23:35 | { id: v } |
#select
-| typedClient.ts:14:24:14:32 | { id: v } | typedClient.ts:13:22:13:29 | req.body | typedClient.ts:14:24:14:32 | { id: v } | This query depends on a $@. | typedClient.ts:13:22:13:29 | req.body | user-provided value |
-| typedClient.ts:22:27:22:35 | { id: v } | typedClient.ts:21:22:21:29 | req.body | typedClient.ts:22:27:22:35 | { id: v } | This query depends on a $@. | typedClient.ts:21:22:21:29 | req.body | user-provided value |
-| typedClient.ts:23:27:23:35 | { id: v } | typedClient.ts:21:22:21:29 | req.body | typedClient.ts:23:27:23:35 | { id: v } | This query depends on a $@. | typedClient.ts:21:22:21:29 | req.body | user-provided value |
+| typedClient.ts:14:24:14:32 | { id: v } | typedClient.ts:13:22:13:29 | req.body | typedClient.ts:14:24:14:32 | { id: v } | This query object depends on a $@. | typedClient.ts:13:22:13:29 | req.body | user-provided value |
+| typedClient.ts:22:27:22:35 | { id: v } | typedClient.ts:21:22:21:29 | req.body | typedClient.ts:22:27:22:35 | { id: v } | This query object depends on a $@. | typedClient.ts:21:22:21:29 | req.body | user-provided value |
+| typedClient.ts:23:27:23:35 | { id: v } | typedClient.ts:21:22:21:29 | req.body | typedClient.ts:23:27:23:35 | { id: v } | This query object depends on a $@. | typedClient.ts:21:22:21:29 | req.body | user-provided value |
diff --git a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected
index be40ab490ad..6eba7711032 100644
--- a/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-089/untyped/SqlInjection.expected
@@ -928,128 +928,128 @@ edges
| tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' |
| tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' |
#select
-| graphql.js:10:34:20:5 | `\\n ... }\\n ` | graphql.js:8:16:8:28 | req.params.id | graphql.js:10:34:20:5 | `\\n ... }\\n ` | This query depends on a $@. | graphql.js:8:16:8:28 | req.params.id | user-provided value |
-| graphql.js:27:30:27:40 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:27:30:27:40 | `foo ${id}` | This query depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
-| graphql.js:30:32:30:42 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:30:32:30:42 | `foo ${id}` | This query depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
-| graphql.js:33:18:33:28 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:33:18:33:28 | `foo ${id}` | This query depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
-| graphql.js:44:14:44:24 | `foo ${id}` | graphql.js:39:16:39:28 | req.params.id | graphql.js:44:14:44:24 | `foo ${id}` | This query depends on a $@. | graphql.js:39:16:39:28 | req.params.id | user-provided value |
-| graphql.js:48:44:48:54 | `foo ${id}` | graphql.js:39:16:39:28 | req.params.id | graphql.js:48:44:48:54 | `foo ${id}` | This query depends on a $@. | graphql.js:39:16:39:28 | req.params.id | user-provided value |
-| graphql.js:56:39:56:49 | `foo ${id}` | graphql.js:55:16:55:28 | req.params.id | graphql.js:56:39:56:49 | `foo ${id}` | This query depends on a $@. | graphql.js:55:16:55:28 | req.params.id | user-provided value |
-| graphql.js:58:66:58:76 | `foo ${id}` | graphql.js:55:16:55:28 | req.params.id | graphql.js:58:66:58:76 | `foo ${id}` | This query depends on a $@. | graphql.js:55:16:55:28 | req.params.id | user-provided value |
-| graphql.js:75:46:75:64 | "{ foo" + id + " }" | graphql.js:74:14:74:25 | req.query.id | graphql.js:75:46:75:64 | "{ foo" + id + " }" | This query depends on a $@. | graphql.js:74:14:74:25 | req.query.id | user-provided value |
-| graphql.js:84:14:90:8 | `{\\n ... }` | graphql.js:74:14:74:25 | req.query.id | graphql.js:84:14:90:8 | `{\\n ... }` | This query depends on a $@. | graphql.js:74:14:74:25 | req.query.id | user-provided value |
-| graphql.js:120:38:120:48 | `foo ${id}` | graphql.js:119:16:119:28 | req.params.id | graphql.js:120:38:120:48 | `foo ${id}` | This query depends on a $@. | graphql.js:119:16:119:28 | req.params.id | user-provided value |
-| html-sanitizer.js:16:9:16:59 | `SELECT ... param1 | html-sanitizer.js:13:39:13:44 | param1 | html-sanitizer.js:16:9:16:59 | `SELECT ... param1 | This query depends on a $@. | html-sanitizer.js:13:39:13:44 | param1 | user-provided value |
-| json-schema-validator.js:33:22:33:26 | query | json-schema-validator.js:25:34:25:47 | req.query.data | json-schema-validator.js:33:22:33:26 | query | This query depends on a $@. | json-schema-validator.js:25:34:25:47 | req.query.data | user-provided value |
-| json-schema-validator.js:35:18:35:22 | query | json-schema-validator.js:25:34:25:47 | req.query.data | json-schema-validator.js:35:18:35:22 | query | This query depends on a $@. | json-schema-validator.js:25:34:25:47 | req.query.data | user-provided value |
-| json-schema-validator.js:55:22:55:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:55:22:55:26 | query | This query depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
-| json-schema-validator.js:59:22:59:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:59:22:59:26 | query | This query depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
-| json-schema-validator.js:61:22:61:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:61:22:61:26 | query | This query depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
-| ldap.js:28:30:28:34 | opts1 | ldap.js:20:21:20:27 | req.url | ldap.js:28:30:28:34 | opts1 | This query depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
-| ldap.js:32:5:32:61 | { filte ... e}))` } | ldap.js:20:21:20:27 | req.url | ldap.js:32:5:32:61 | { filte ... e}))` } | This query depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
-| ldap.js:66:30:66:53 | { filte ... ilter } | ldap.js:20:21:20:27 | req.url | ldap.js:66:30:66:53 | { filte ... ilter } | This query depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
-| ldap.js:68:27:68:42 | `cn=${username}` | ldap.js:20:21:20:27 | req.url | ldap.js:68:27:68:42 | `cn=${username}` | This query depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
-| marsdb-flow-to.js:14:17:14:21 | query | marsdb-flow-to.js:11:17:11:24 | req.body | marsdb-flow-to.js:14:17:14:21 | query | This query depends on a $@. | marsdb-flow-to.js:11:17:11:24 | req.body | user-provided value |
-| marsdb.js:16:12:16:16 | query | marsdb.js:13:17:13:24 | req.body | marsdb.js:16:12:16:16 | query | This query depends on a $@. | marsdb.js:13:17:13:24 | req.body | user-provided value |
-| minimongo.js:18:12:18:16 | query | minimongo.js:15:17:15:24 | req.body | minimongo.js:18:12:18:16 | query | This query depends on a $@. | minimongo.js:15:17:15:24 | req.body | user-provided value |
-| mongodb.js:18:16:18:20 | query | mongodb.js:13:19:13:26 | req.body | mongodb.js:18:16:18:20 | query | This query depends on a $@. | mongodb.js:13:19:13:26 | req.body | user-provided value |
-| mongodb.js:32:18:32:45 | { title ... itle) } | mongodb.js:26:19:26:26 | req.body | mongodb.js:32:18:32:45 | { title ... itle) } | This query depends on a $@. | mongodb.js:26:19:26:26 | req.body | user-provided value |
-| mongodb.js:54:16:54:20 | query | mongodb.js:49:19:49:33 | req.query.title | mongodb.js:54:16:54:20 | query | This query depends on a $@. | mongodb.js:49:19:49:33 | req.query.title | user-provided value |
-| mongodb.js:65:12:65:16 | query | mongodb.js:60:16:60:30 | req.query.title | mongodb.js:65:12:65:16 | query | This query depends on a $@. | mongodb.js:60:16:60:30 | req.query.title | user-provided value |
-| mongodb.js:77:14:77:26 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:77:14:77:26 | { tags: tag } | This query depends on a $@. | mongodb.js:70:13:70:25 | req.query.tag | user-provided value |
-| mongodb.js:85:12:85:24 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:85:12:85:24 | { tags: tag } | This query depends on a $@. | mongodb.js:70:13:70:25 | req.query.tag | user-provided value |
-| mongodb.js:112:14:112:18 | query | mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | This query depends on a $@. | mongodb.js:107:17:107:29 | queries.title | user-provided value |
-| mongodb_bodySafe.js:29:16:29:20 | query | mongodb_bodySafe.js:24:19:24:33 | req.query.title | mongodb_bodySafe.js:29:16:29:20 | query | This query depends on a $@. | mongodb_bodySafe.js:24:19:24:33 | req.query.title | user-provided value |
-| mongoose.js:24:24:24:30 | [query] | mongoose.js:21:19:21:26 | req.body | mongoose.js:24:24:24:30 | [query] | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:27:20:27:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:27:20:27:24 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:30:25:30:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:30:25:30:29 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:33:24:33:28 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:33:24:33:28 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:36:31:36:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:36:31:36:35 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:39:19:39:23 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:39:19:39:23 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:42:22:42:26 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:42:22:42:26 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:45:31:45:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:45:31:45:35 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:48:31:48:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:48:31:48:35 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:51:31:51:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:51:31:51:35 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:54:25:54:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:54:25:54:29 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:57:21:57:25 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:57:21:57:25 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:60:25:60:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:60:25:60:29 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:63:21:63:25 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:63:21:63:25 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:65:32:65:36 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:65:32:65:36 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:67:27:67:31 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:67:27:67:31 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:68:8:68:12 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:68:8:68:12 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:71:20:71:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:71:20:71:24 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:72:16:72:20 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:72:16:72:20 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:73:8:73:12 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:73:8:73:12 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:74:7:74:11 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:74:7:74:11 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:75:16:75:20 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:75:16:75:20 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:77:10:77:14 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:77:10:77:14 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:82:46:82:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:82:46:82:50 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:83:47:83:51 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:83:47:83:51 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:85:46:85:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:85:46:85:50 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:87:51:87:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:87:51:87:55 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:89:46:89:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:89:46:89:50 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:92:46:92:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:92:46:92:50 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:94:51:94:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:94:51:94:55 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:96:46:96:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:96:46:96:50 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:111:14:111:18 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:111:14:111:18 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:113:31:113:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:113:31:113:35 | query | This query depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
-| mongoose.js:116:22:116:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:116:22:116:25 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:117:21:117:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:117:21:117:24 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:118:21:118:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:118:21:118:24 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:119:18:119:21 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:119:18:119:21 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:120:22:120:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:120:22:120:25 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:121:16:121:19 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:121:16:121:19 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:122:19:122:22 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:122:19:122:22 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:123:20:123:21 | id | mongoose.js:115:11:115:22 | req.query.id | mongoose.js:123:20:123:21 | id | This query depends on a $@. | mongoose.js:115:11:115:22 | req.query.id | user-provided value |
-| mongoose.js:124:28:124:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:124:28:124:31 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:125:28:125:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:125:28:125:31 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:126:28:126:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:126:28:126:31 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:127:18:127:21 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:127:18:127:21 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:128:22:128:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:128:22:128:25 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:129:21:129:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:129:21:129:24 | cond | This query depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
-| mongoose.js:130:16:130:26 | { _id: id } | mongoose.js:115:11:115:22 | req.query.id | mongoose.js:130:16:130:26 | { _id: id } | This query depends on a $@. | mongoose.js:115:11:115:22 | req.query.id | user-provided value |
-| mongooseJsonParse.js:23:19:23:23 | query | mongooseJsonParse.js:20:30:20:43 | req.query.data | mongooseJsonParse.js:23:19:23:23 | query | This query depends on a $@. | mongooseJsonParse.js:20:30:20:43 | req.query.data | user-provided value |
-| mongooseModelClient.js:11:16:11:24 | { id: v } | mongooseModelClient.js:10:22:10:29 | req.body | mongooseModelClient.js:11:16:11:24 | { id: v } | This query depends on a $@. | mongooseModelClient.js:10:22:10:29 | req.body | user-provided value |
-| mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | mongooseModelClient.js:12:22:12:29 | req.body | mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | This query depends on a $@. | mongooseModelClient.js:12:22:12:29 | req.body | user-provided value |
-| mysql.js:15:18:15:65 | 'SELECT ... + temp | mysql.js:6:16:6:31 | req.params.value | mysql.js:15:18:15:65 | 'SELECT ... + temp | This query depends on a $@. | mysql.js:6:16:6:31 | req.params.value | user-provided value |
-| mysql.js:19:26:19:73 | 'SELECT ... + temp | mysql.js:6:16:6:31 | req.params.value | mysql.js:19:26:19:73 | 'SELECT ... + temp | This query depends on a $@. | mysql.js:6:16:6:31 | req.params.value | user-provided value |
-| pg-promise-types.ts:8:17:8:21 | taint | pg-promise-types.ts:7:17:7:28 | req.params.x | pg-promise-types.ts:8:17:8:21 | taint | This query depends on a $@. | pg-promise-types.ts:7:17:7:28 | req.params.x | user-provided value |
-| pg-promise.js:9:10:9:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:9:10:9:14 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:10:11:10:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:10:11:10:15 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:11:17:11:21 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:11:17:11:21 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:12:10:12:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:12:10:12:14 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:13:12:13:16 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:13:12:13:16 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:14:18:14:22 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:14:18:14:22 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:15:11:15:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:15:11:15:15 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:16:10:16:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:16:10:16:14 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:17:16:17:20 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:17:16:17:20 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:18:12:18:16 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:18:12:18:16 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:19:13:19:17 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:19:13:19:17 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:22:11:22:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:22:11:22:15 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:30:13:30:25 | req.params.id | pg-promise.js:30:13:30:25 | req.params.id | pg-promise.js:30:13:30:25 | req.params.id | This query depends on a $@. | pg-promise.js:30:13:30:25 | req.params.id | user-provided value |
-| pg-promise.js:34:13:34:25 | req.params.id | pg-promise.js:34:13:34:25 | req.params.id | pg-promise.js:34:13:34:25 | req.params.id | This query depends on a $@. | pg-promise.js:34:13:34:25 | req.params.id | user-provided value |
-| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query depends on a $@. | pg-promise.js:39:7:39:19 | req.params.id | user-provided value |
-| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query depends on a $@. | pg-promise.js:40:7:40:21 | req.params.name | user-provided value |
-| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:41:7:41:20 | req.params.foo | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query depends on a $@. | pg-promise.js:41:7:41:20 | req.params.foo | user-provided value |
-| pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:39:7:39:19 | req.params.id | This query depends on a $@. | pg-promise.js:39:7:39:19 | req.params.id | user-provided value |
-| pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:40:7:40:21 | req.params.name | This query depends on a $@. | pg-promise.js:40:7:40:21 | req.params.name | user-provided value |
-| pg-promise.js:47:11:47:23 | req.params.id | pg-promise.js:47:11:47:23 | req.params.id | pg-promise.js:47:11:47:23 | req.params.id | This query depends on a $@. | pg-promise.js:47:11:47:23 | req.params.id | user-provided value |
-| pg-promise.js:54:11:54:23 | req.params.id | pg-promise.js:54:11:54:23 | req.params.id | pg-promise.js:54:11:54:23 | req.params.id | This query depends on a $@. | pg-promise.js:54:11:54:23 | req.params.id | user-provided value |
-| pg-promise.js:56:14:56:29 | req.params.title | pg-promise.js:56:14:56:29 | req.params.title | pg-promise.js:56:14:56:29 | req.params.title | This query depends on a $@. | pg-promise.js:56:14:56:29 | req.params.title | user-provided value |
-| pg-promise.js:60:20:60:24 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:60:20:60:24 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:63:23:63:27 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:63:23:63:27 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| pg-promise.js:64:16:64:20 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:64:16:64:20 | query | This query depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
-| redis.js:10:16:10:27 | req.body.key | redis.js:10:16:10:23 | req.body | redis.js:10:16:10:27 | req.body.key | This query depends on a $@. | redis.js:10:16:10:23 | req.body | user-provided value |
-| redis.js:18:16:18:18 | key | redis.js:12:15:12:22 | req.body | redis.js:18:16:18:18 | key | This query depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
-| redis.js:19:43:19:45 | key | redis.js:12:15:12:22 | req.body | redis.js:19:43:19:45 | key | This query depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
-| redis.js:25:14:25:16 | key | redis.js:12:15:12:22 | req.body | redis.js:25:14:25:16 | key | This query depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
-| redis.js:30:23:30:25 | key | redis.js:12:15:12:22 | req.body | redis.js:30:23:30:25 | key | This query depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
-| redis.js:32:28:32:30 | key | redis.js:12:15:12:22 | req.body | redis.js:32:28:32:30 | key | This query depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
-| redis.js:39:16:39:18 | key | redis.js:38:17:38:24 | req.body | redis.js:39:16:39:18 | key | This query depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
-| redis.js:43:27:43:29 | key | redis.js:38:17:38:24 | req.body | redis.js:43:27:43:29 | key | This query depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
-| redis.js:46:34:46:36 | key | redis.js:38:17:38:24 | req.body | redis.js:46:34:46:36 | key | This query depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
-| socketio.js:11:12:11:53 | `INSERT ... andle}` | socketio.js:10:25:10:30 | handle | socketio.js:11:12:11:53 | `INSERT ... andle}` | This query depends on a $@. | socketio.js:10:25:10:30 | handle | user-provided value |
-| tst2.js:9:27:9:84 | "select ... d + "'" | tst2.js:9:66:9:78 | req.params.id | tst2.js:9:27:9:84 | "select ... d + "'" | This query depends on a $@. | tst2.js:9:66:9:78 | req.params.id | user-provided value |
-| tst3.js:9:14:9:19 | query1 | tst3.js:8:16:8:34 | req.params.category | tst3.js:9:14:9:19 | query1 | This query depends on a $@. | tst3.js:8:16:8:34 | req.params.category | user-provided value |
-| tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value |
-| tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value |
+| graphql.js:10:34:20:5 | `\\n ... }\\n ` | graphql.js:8:16:8:28 | req.params.id | graphql.js:10:34:20:5 | `\\n ... }\\n ` | This query string depends on a $@. | graphql.js:8:16:8:28 | req.params.id | user-provided value |
+| graphql.js:27:30:27:40 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:27:30:27:40 | `foo ${id}` | This query string depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
+| graphql.js:30:32:30:42 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:30:32:30:42 | `foo ${id}` | This query string depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
+| graphql.js:33:18:33:28 | `foo ${id}` | graphql.js:26:16:26:28 | req.params.id | graphql.js:33:18:33:28 | `foo ${id}` | This query string depends on a $@. | graphql.js:26:16:26:28 | req.params.id | user-provided value |
+| graphql.js:44:14:44:24 | `foo ${id}` | graphql.js:39:16:39:28 | req.params.id | graphql.js:44:14:44:24 | `foo ${id}` | This query string depends on a $@. | graphql.js:39:16:39:28 | req.params.id | user-provided value |
+| graphql.js:48:44:48:54 | `foo ${id}` | graphql.js:39:16:39:28 | req.params.id | graphql.js:48:44:48:54 | `foo ${id}` | This query string depends on a $@. | graphql.js:39:16:39:28 | req.params.id | user-provided value |
+| graphql.js:56:39:56:49 | `foo ${id}` | graphql.js:55:16:55:28 | req.params.id | graphql.js:56:39:56:49 | `foo ${id}` | This query string depends on a $@. | graphql.js:55:16:55:28 | req.params.id | user-provided value |
+| graphql.js:58:66:58:76 | `foo ${id}` | graphql.js:55:16:55:28 | req.params.id | graphql.js:58:66:58:76 | `foo ${id}` | This query string depends on a $@. | graphql.js:55:16:55:28 | req.params.id | user-provided value |
+| graphql.js:75:46:75:64 | "{ foo" + id + " }" | graphql.js:74:14:74:25 | req.query.id | graphql.js:75:46:75:64 | "{ foo" + id + " }" | This query string depends on a $@. | graphql.js:74:14:74:25 | req.query.id | user-provided value |
+| graphql.js:84:14:90:8 | `{\\n ... }` | graphql.js:74:14:74:25 | req.query.id | graphql.js:84:14:90:8 | `{\\n ... }` | This query string depends on a $@. | graphql.js:74:14:74:25 | req.query.id | user-provided value |
+| graphql.js:120:38:120:48 | `foo ${id}` | graphql.js:119:16:119:28 | req.params.id | graphql.js:120:38:120:48 | `foo ${id}` | This query string depends on a $@. | graphql.js:119:16:119:28 | req.params.id | user-provided value |
+| html-sanitizer.js:16:9:16:59 | `SELECT ... param1 | html-sanitizer.js:13:39:13:44 | param1 | html-sanitizer.js:16:9:16:59 | `SELECT ... param1 | This query string depends on a $@. | html-sanitizer.js:13:39:13:44 | param1 | user-provided value |
+| json-schema-validator.js:33:22:33:26 | query | json-schema-validator.js:25:34:25:47 | req.query.data | json-schema-validator.js:33:22:33:26 | query | This query object depends on a $@. | json-schema-validator.js:25:34:25:47 | req.query.data | user-provided value |
+| json-schema-validator.js:35:18:35:22 | query | json-schema-validator.js:25:34:25:47 | req.query.data | json-schema-validator.js:35:18:35:22 | query | This query object depends on a $@. | json-schema-validator.js:25:34:25:47 | req.query.data | user-provided value |
+| json-schema-validator.js:55:22:55:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:55:22:55:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
+| json-schema-validator.js:59:22:59:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:59:22:59:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
+| json-schema-validator.js:61:22:61:26 | query | json-schema-validator.js:50:34:50:47 | req.query.data | json-schema-validator.js:61:22:61:26 | query | This query object depends on a $@. | json-schema-validator.js:50:34:50:47 | req.query.data | user-provided value |
+| ldap.js:28:30:28:34 | opts1 | ldap.js:20:21:20:27 | req.url | ldap.js:28:30:28:34 | opts1 | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
+| ldap.js:32:5:32:61 | { filte ... e}))` } | ldap.js:20:21:20:27 | req.url | ldap.js:32:5:32:61 | { filte ... e}))` } | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
+| ldap.js:66:30:66:53 | { filte ... ilter } | ldap.js:20:21:20:27 | req.url | ldap.js:66:30:66:53 | { filte ... ilter } | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
+| ldap.js:68:27:68:42 | `cn=${username}` | ldap.js:20:21:20:27 | req.url | ldap.js:68:27:68:42 | `cn=${username}` | This query string depends on a $@. | ldap.js:20:21:20:27 | req.url | user-provided value |
+| marsdb-flow-to.js:14:17:14:21 | query | marsdb-flow-to.js:11:17:11:24 | req.body | marsdb-flow-to.js:14:17:14:21 | query | This query object depends on a $@. | marsdb-flow-to.js:11:17:11:24 | req.body | user-provided value |
+| marsdb.js:16:12:16:16 | query | marsdb.js:13:17:13:24 | req.body | marsdb.js:16:12:16:16 | query | This query object depends on a $@. | marsdb.js:13:17:13:24 | req.body | user-provided value |
+| minimongo.js:18:12:18:16 | query | minimongo.js:15:17:15:24 | req.body | minimongo.js:18:12:18:16 | query | This query object depends on a $@. | minimongo.js:15:17:15:24 | req.body | user-provided value |
+| mongodb.js:18:16:18:20 | query | mongodb.js:13:19:13:26 | req.body | mongodb.js:18:16:18:20 | query | This query object depends on a $@. | mongodb.js:13:19:13:26 | req.body | user-provided value |
+| mongodb.js:32:18:32:45 | { title ... itle) } | mongodb.js:26:19:26:26 | req.body | mongodb.js:32:18:32:45 | { title ... itle) } | This query object depends on a $@. | mongodb.js:26:19:26:26 | req.body | user-provided value |
+| mongodb.js:54:16:54:20 | query | mongodb.js:49:19:49:33 | req.query.title | mongodb.js:54:16:54:20 | query | This query object depends on a $@. | mongodb.js:49:19:49:33 | req.query.title | user-provided value |
+| mongodb.js:65:12:65:16 | query | mongodb.js:60:16:60:30 | req.query.title | mongodb.js:65:12:65:16 | query | This query object depends on a $@. | mongodb.js:60:16:60:30 | req.query.title | user-provided value |
+| mongodb.js:77:14:77:26 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:77:14:77:26 | { tags: tag } | This query object depends on a $@. | mongodb.js:70:13:70:25 | req.query.tag | user-provided value |
+| mongodb.js:85:12:85:24 | { tags: tag } | mongodb.js:70:13:70:25 | req.query.tag | mongodb.js:85:12:85:24 | { tags: tag } | This query object depends on a $@. | mongodb.js:70:13:70:25 | req.query.tag | user-provided value |
+| mongodb.js:112:14:112:18 | query | mongodb.js:107:17:107:29 | queries.title | mongodb.js:112:14:112:18 | query | This query object depends on a $@. | mongodb.js:107:17:107:29 | queries.title | user-provided value |
+| mongodb_bodySafe.js:29:16:29:20 | query | mongodb_bodySafe.js:24:19:24:33 | req.query.title | mongodb_bodySafe.js:29:16:29:20 | query | This query object depends on a $@. | mongodb_bodySafe.js:24:19:24:33 | req.query.title | user-provided value |
+| mongoose.js:24:24:24:30 | [query] | mongoose.js:21:19:21:26 | req.body | mongoose.js:24:24:24:30 | [query] | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:27:20:27:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:27:20:27:24 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:30:25:30:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:30:25:30:29 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:33:24:33:28 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:33:24:33:28 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:36:31:36:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:36:31:36:35 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:39:19:39:23 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:39:19:39:23 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:42:22:42:26 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:42:22:42:26 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:45:31:45:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:45:31:45:35 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:48:31:48:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:48:31:48:35 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:51:31:51:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:51:31:51:35 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:54:25:54:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:54:25:54:29 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:57:21:57:25 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:57:21:57:25 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:60:25:60:29 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:60:25:60:29 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:63:21:63:25 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:63:21:63:25 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:65:32:65:36 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:65:32:65:36 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:67:27:67:31 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:67:27:67:31 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:68:8:68:12 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:68:8:68:12 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:71:20:71:24 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:71:20:71:24 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:72:16:72:20 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:72:16:72:20 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:73:8:73:12 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:73:8:73:12 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:74:7:74:11 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:74:7:74:11 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:75:16:75:20 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:75:16:75:20 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:77:10:77:14 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:77:10:77:14 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:82:46:82:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:82:46:82:50 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:83:47:83:51 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:83:47:83:51 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:85:46:85:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:85:46:85:50 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:87:51:87:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:87:51:87:55 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:89:46:89:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:89:46:89:50 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:92:46:92:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:92:46:92:50 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:94:51:94:55 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:94:51:94:55 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:96:46:96:50 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:96:46:96:50 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:111:14:111:18 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:111:14:111:18 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:113:31:113:35 | query | mongoose.js:21:19:21:26 | req.body | mongoose.js:113:31:113:35 | query | This query object depends on a $@. | mongoose.js:21:19:21:26 | req.body | user-provided value |
+| mongoose.js:116:22:116:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:116:22:116:25 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:117:21:117:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:117:21:117:24 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:118:21:118:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:118:21:118:24 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:119:18:119:21 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:119:18:119:21 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:120:22:120:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:120:22:120:25 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:121:16:121:19 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:121:16:121:19 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:122:19:122:22 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:122:19:122:22 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:123:20:123:21 | id | mongoose.js:115:11:115:22 | req.query.id | mongoose.js:123:20:123:21 | id | This query object depends on a $@. | mongoose.js:115:11:115:22 | req.query.id | user-provided value |
+| mongoose.js:124:28:124:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:124:28:124:31 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:125:28:125:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:125:28:125:31 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:126:28:126:31 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:126:28:126:31 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:127:18:127:21 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:127:18:127:21 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:128:22:128:25 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:128:22:128:25 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:129:21:129:24 | cond | mongoose.js:115:32:115:45 | req.query.cond | mongoose.js:129:21:129:24 | cond | This query object depends on a $@. | mongoose.js:115:32:115:45 | req.query.cond | user-provided value |
+| mongoose.js:130:16:130:26 | { _id: id } | mongoose.js:115:11:115:22 | req.query.id | mongoose.js:130:16:130:26 | { _id: id } | This query object depends on a $@. | mongoose.js:115:11:115:22 | req.query.id | user-provided value |
+| mongooseJsonParse.js:23:19:23:23 | query | mongooseJsonParse.js:20:30:20:43 | req.query.data | mongooseJsonParse.js:23:19:23:23 | query | This query object depends on a $@. | mongooseJsonParse.js:20:30:20:43 | req.query.data | user-provided value |
+| mongooseModelClient.js:11:16:11:24 | { id: v } | mongooseModelClient.js:10:22:10:29 | req.body | mongooseModelClient.js:11:16:11:24 | { id: v } | This query object depends on a $@. | mongooseModelClient.js:10:22:10:29 | req.body | user-provided value |
+| mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | mongooseModelClient.js:12:22:12:29 | req.body | mongooseModelClient.js:12:16:12:34 | { id: req.body.id } | This query object depends on a $@. | mongooseModelClient.js:12:22:12:29 | req.body | user-provided value |
+| mysql.js:15:18:15:65 | 'SELECT ... + temp | mysql.js:6:16:6:31 | req.params.value | mysql.js:15:18:15:65 | 'SELECT ... + temp | This query string depends on a $@. | mysql.js:6:16:6:31 | req.params.value | user-provided value |
+| mysql.js:19:26:19:73 | 'SELECT ... + temp | mysql.js:6:16:6:31 | req.params.value | mysql.js:19:26:19:73 | 'SELECT ... + temp | This query string depends on a $@. | mysql.js:6:16:6:31 | req.params.value | user-provided value |
+| pg-promise-types.ts:8:17:8:21 | taint | pg-promise-types.ts:7:17:7:28 | req.params.x | pg-promise-types.ts:8:17:8:21 | taint | This query string depends on a $@. | pg-promise-types.ts:7:17:7:28 | req.params.x | user-provided value |
+| pg-promise.js:9:10:9:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:9:10:9:14 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:10:11:10:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:10:11:10:15 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:11:17:11:21 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:11:17:11:21 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:12:10:12:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:12:10:12:14 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:13:12:13:16 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:13:12:13:16 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:14:18:14:22 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:14:18:14:22 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:15:11:15:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:15:11:15:15 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:16:10:16:14 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:16:10:16:14 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:17:16:17:20 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:17:16:17:20 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:18:12:18:16 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:18:12:18:16 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:19:13:19:17 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:19:13:19:17 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:22:11:22:15 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:22:11:22:15 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:30:13:30:25 | req.params.id | pg-promise.js:30:13:30:25 | req.params.id | pg-promise.js:30:13:30:25 | req.params.id | This query string depends on a $@. | pg-promise.js:30:13:30:25 | req.params.id | user-provided value |
+| pg-promise.js:34:13:34:25 | req.params.id | pg-promise.js:34:13:34:25 | req.params.id | pg-promise.js:34:13:34:25 | req.params.id | This query string depends on a $@. | pg-promise.js:34:13:34:25 | req.params.id | user-provided value |
+| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query string depends on a $@. | pg-promise.js:39:7:39:19 | req.params.id | user-provided value |
+| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query string depends on a $@. | pg-promise.js:40:7:40:21 | req.params.name | user-provided value |
+| pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | pg-promise.js:41:7:41:20 | req.params.foo | pg-promise.js:38:13:42:5 | [\\n ... n\\n ] | This query string depends on a $@. | pg-promise.js:41:7:41:20 | req.params.foo | user-provided value |
+| pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:39:7:39:19 | req.params.id | pg-promise.js:39:7:39:19 | req.params.id | This query string depends on a $@. | pg-promise.js:39:7:39:19 | req.params.id | user-provided value |
+| pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:40:7:40:21 | req.params.name | pg-promise.js:40:7:40:21 | req.params.name | This query string depends on a $@. | pg-promise.js:40:7:40:21 | req.params.name | user-provided value |
+| pg-promise.js:47:11:47:23 | req.params.id | pg-promise.js:47:11:47:23 | req.params.id | pg-promise.js:47:11:47:23 | req.params.id | This query string depends on a $@. | pg-promise.js:47:11:47:23 | req.params.id | user-provided value |
+| pg-promise.js:54:11:54:23 | req.params.id | pg-promise.js:54:11:54:23 | req.params.id | pg-promise.js:54:11:54:23 | req.params.id | This query string depends on a $@. | pg-promise.js:54:11:54:23 | req.params.id | user-provided value |
+| pg-promise.js:56:14:56:29 | req.params.title | pg-promise.js:56:14:56:29 | req.params.title | pg-promise.js:56:14:56:29 | req.params.title | This query string depends on a $@. | pg-promise.js:56:14:56:29 | req.params.title | user-provided value |
+| pg-promise.js:60:20:60:24 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:60:20:60:24 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:63:23:63:27 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:63:23:63:27 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| pg-promise.js:64:16:64:20 | query | pg-promise.js:7:16:7:34 | req.params.category | pg-promise.js:64:16:64:20 | query | This query string depends on a $@. | pg-promise.js:7:16:7:34 | req.params.category | user-provided value |
+| redis.js:10:16:10:27 | req.body.key | redis.js:10:16:10:23 | req.body | redis.js:10:16:10:27 | req.body.key | This query object depends on a $@. | redis.js:10:16:10:23 | req.body | user-provided value |
+| redis.js:18:16:18:18 | key | redis.js:12:15:12:22 | req.body | redis.js:18:16:18:18 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
+| redis.js:19:43:19:45 | key | redis.js:12:15:12:22 | req.body | redis.js:19:43:19:45 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
+| redis.js:25:14:25:16 | key | redis.js:12:15:12:22 | req.body | redis.js:25:14:25:16 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
+| redis.js:30:23:30:25 | key | redis.js:12:15:12:22 | req.body | redis.js:30:23:30:25 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
+| redis.js:32:28:32:30 | key | redis.js:12:15:12:22 | req.body | redis.js:32:28:32:30 | key | This query object depends on a $@. | redis.js:12:15:12:22 | req.body | user-provided value |
+| redis.js:39:16:39:18 | key | redis.js:38:17:38:24 | req.body | redis.js:39:16:39:18 | key | This query object depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
+| redis.js:43:27:43:29 | key | redis.js:38:17:38:24 | req.body | redis.js:43:27:43:29 | key | This query object depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
+| redis.js:46:34:46:36 | key | redis.js:38:17:38:24 | req.body | redis.js:46:34:46:36 | key | This query object depends on a $@. | redis.js:38:17:38:24 | req.body | user-provided value |
+| socketio.js:11:12:11:53 | `INSERT ... andle}` | socketio.js:10:25:10:30 | handle | socketio.js:11:12:11:53 | `INSERT ... andle}` | This query string depends on a $@. | socketio.js:10:25:10:30 | handle | user-provided value |
+| tst2.js:9:27:9:84 | "select ... d + "'" | tst2.js:9:66:9:78 | req.params.id | tst2.js:9:27:9:84 | "select ... d + "'" | This query string depends on a $@. | tst2.js:9:66:9:78 | req.params.id | user-provided value |
+| tst3.js:9:14:9:19 | query1 | tst3.js:8:16:8:34 | req.params.category | tst3.js:9:14:9:19 | query1 | This query string depends on a $@. | tst3.js:8:16:8:34 | req.params.category | user-provided value |
+| tst4.js:8:10:8:66 | 'SELECT ... d + '"' | tst4.js:8:46:8:60 | $routeParams.id | tst4.js:8:10:8:66 | 'SELECT ... d + '"' | This query string depends on a $@. | tst4.js:8:46:8:60 | $routeParams.id | user-provided value |
+| tst.js:10:10:10:64 | 'SELECT ... d + '"' | tst.js:10:46:10:58 | req.params.id | tst.js:10:10:10:64 | 'SELECT ... d + '"' | This query string depends on a $@. | tst.js:10:46:10:58 | req.params.id | user-provided value |
From fe26aca238ad02cd26198bca998a68066e581d94 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 31 May 2023 08:54:24 +0200
Subject: [PATCH 196/813] Remove non-ASCII character
---
.../code/java/frameworks/google/GsonSerializability.qll | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
index dba25be7b22..1e41ad0c458 100644
--- a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
@@ -27,8 +27,8 @@ private class ExplicitlyReadGsonDeserializableType extends GsonDeserializableTyp
ma.getMethod() instanceof GsonReadValueMethod and
// ...where `this` is used in the final argument, indicating that this type will be deserialized.
// TODO: find a way to get the type represented by java.lang.reflect.Type and com.google.gson.reflect.TypeToken
- // fromJson(String json, TypeToken typeOfT)
- // fromJson(String json, Type typeOfT)
+ // fromJson(String json, TypeToken typeOfT)
+ // fromJson(String json, Type typeOfT)
usesType(ma.getArgument(1).getType(), this) and
not this instanceof TypeClass and
not this instanceof TypeObject
From e24b45b4233724e6756057f87c04593bed583c5f Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Wed, 31 May 2023 09:57:38 +0200
Subject: [PATCH 197/813] elaborate on both SQL and NoSQL injection in the
js/sql-injection qhelp
---
.../Security/CWE-089/SqlInjection.inc.qhelp | 48 ++++++++++++++-----
.../CWE-089/examples/NoSqlInjection.js | 18 +++++++
.../CWE-089/examples/NoSqlInjectionFix.js | 21 ++++++++
.../Security/CWE-089/examples/SqlInjection.js | 7 ---
.../CWE-089/examples/SqlInjectionFix.js | 12 +++++
5 files changed, 86 insertions(+), 20 deletions(-)
create mode 100644 javascript/ql/src/Security/CWE-089/examples/NoSqlInjection.js
create mode 100644 javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
create mode 100644 javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
index 8cdc2419d47..1d99428b718 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
@@ -20,7 +20,13 @@ or prepared statements.
For NoSQL queries, make use of an operator like MongoDB's $eq
to ensure that untrusted data is interpreted as a literal value and not as
-a query object.
+a query object. Alternatively, check that the untrusted data is a literal
+value and not a query object before using it in a query.
+
+
+For SQL queries, use query parameters or prepared statements to
+embed untrusted data into the query string, or use a library like
+sqlstring to escape untrusted data.
@@ -32,31 +38,47 @@ an HTTP request handler in a web application, whose parameter
-The handler constructs two copies of the same SQL query involving
-user input taken from the request object, once unsafely using
-string concatenation, and once safely using query parameters.
+The handler constructs constructs an SQL query string from user input
+and executes it as a database query using the pg library.
+THe user input may contain quote characters, so this code is vulnerable
+to a SQL injection attack.
-
-In the first case, the query string query1 is built by
-directly concatenating a user-supplied request parameter with some
-string literals. The parameter may include quote characters, so this
-code is vulnerable to a SQL injection attack.
-
+
-In the second case, the parameter is embedded into the query string
-query2 using query parameters. In this example, we use
+To fix this vulnerability, we can use query parameters to embed the
+user input into the query string. In this example, we use
the API offered by the pg Postgres database connector
library, but other libraries offer similar features. This version is
immune to injection attacks.
-
+
+
+
+
+
+In the following example an express handler attempts to delete
+a single document from a MongoDB collection. The document to be
+deleted is identified by its _id field, which is
+constructed from user input. The user input may contain a query
+object, so this code is vulnerable to a NoSQL injection attack.
+
+
+
+
+
+To fix this vulnerability, we can check that the user input is a
+literal value and not a query object before using it in a query.
+
diff --git a/javascript/ql/src/Security/CWE-089/examples/NoSqlInjection.js b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjection.js
new file mode 100644
index 00000000000..8af7550aee4
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjection.js
@@ -0,0 +1,18 @@
+const express = require("express");
+const mongoose = require("mongoose");
+const Todo = mongoose.model(
+ "Todo",
+ new mongoose.Schema({ text: { type: String } }, { timestamps: true })
+);
+
+const app = express();
+app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+
+app.delete("/api/delete", async (req, res) => {
+ let id = req.body.id;
+
+ await Todo.deleteOne({ _id: id }); // BAD: id might be an object with special properties
+
+ res.json({ status: "ok" });
+});
diff --git a/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
new file mode 100644
index 00000000000..fe982168be1
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
@@ -0,0 +1,21 @@
+const express = require("express");
+const mongoose = require("mongoose");
+const Todo = mongoose.model(
+ "Todo",
+ new mongoose.Schema({ text: { type: String } }, { timestamps: true })
+);
+
+const app = express();
+app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+
+app.delete("/api/delete", async (req, res) => {
+ let id = req.body.id;
+ if (typeof id !== "string") {
+ res.status(400).json({ status: "error" });
+ return;
+ }
+ await Todo.deleteOne({ _id: id }); // GOOD: id is guaranteed to be a string
+
+ res.json({ status: "ok" });
+});
diff --git a/javascript/ql/src/Security/CWE-089/examples/SqlInjection.js b/javascript/ql/src/Security/CWE-089/examples/SqlInjection.js
index 113a034219c..9c9197e6f58 100644
--- a/javascript/ql/src/Security/CWE-089/examples/SqlInjection.js
+++ b/javascript/ql/src/Security/CWE-089/examples/SqlInjection.js
@@ -11,11 +11,4 @@ app.get("search", function handler(req, res) {
pool.query(query1, [], function(err, results) {
// process results
});
-
- // GOOD: use parameters
- var query2 =
- "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1" + " ORDER BY PRICE";
- pool.query(query2, [req.params.category], function(err, results) {
- // process results
- });
});
diff --git a/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
new file mode 100644
index 00000000000..4391b83e391
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
@@ -0,0 +1,12 @@
+const app = require("express")(),
+ pg = require("pg"),
+ pool = new pg.Pool(config);
+
+app.get("search", function handler(req, res) {
+ // GOOD: use parameters
+ var query2 =
+ "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1" + " ORDER BY PRICE";
+ pool.query(query2, [req.params.category], function(err, results) {
+ // process results
+ });
+});
From 96bae2d5ecd65111bd98b1c029422bf8b2987835 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 10:34:58 +0200
Subject: [PATCH 198/813] Java: avoid downcasting to DollarAtString
---
...utomodelApplicationModeCharacteristics.qll | 4 ++--
...tomodelApplicationModeExtractCandidates.ql | 17 ++++++++-------
...lApplicationModeExtractNegativeExamples.ql | 16 +++++++-------
...lApplicationModeExtractPositiveExamples.ql | 17 ++++++++-------
.../AutomodelFrameworkModeCharacteristics.qll | 4 ++--
...AutomodelFrameworkModeExtractCandidates.ql | 19 +++++++++--------
...delFrameworkModeExtractNegativeExamples.ql | 21 ++++++++++---------
...delFrameworkModeExtractPositiveExamples.ql | 19 +++++++++--------
8 files changed, 61 insertions(+), 56 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 4e753a4479d..882c957f3a5 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -171,7 +171,7 @@ class ApplicationModeMetadataExtractor extends string {
}
predicate hasMetadata(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
+ Endpoint e, string package, string type, string subtypes, string name, string signature,
string input
) {
exists(Call call, Callable callable, int argIdx |
@@ -184,7 +184,7 @@ class ApplicationModeMetadataExtractor extends string {
input = AutomodelSharedUtil::getArgumentForIndex(argIdx) and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
- subtypes = this.considerSubtypes(callable) and
+ subtypes = this.considerSubtypes(callable).toString() and
name = callable.getName() and
signature = ExternalFlow::paramsString(callable)
)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
index 40e0e307300..589a10ae663 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractCandidates.ql
@@ -16,8 +16,9 @@ private import AutomodelApplicationModeCharacteristics
private import AutomodelSharedUtil
from
- Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, string package,
- string type, boolean subtypes, string name, string signature, string input
+ Endpoint endpoint, string message, ApplicationModeMetadataExtractor meta, DollarAtString package,
+ DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
+ DollarAtString input
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
@@ -40,9 +41,9 @@ where
)
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", // method name
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", // method name
+ signature, "signature", //
+ input, "input" //
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
index d817d3a244a..7407be0be57 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
@@ -35,8 +35,8 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
from
Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
- ApplicationModeMetadataExtractor meta, string package, string type, boolean subtypes, string name,
- string signature, string input
+ ApplicationModeMetadataExtractor meta, DollarAtString package, DollarAtString type,
+ DollarAtString subtypes, DollarAtString name, DollarAtString signature, DollarAtString input
where
endpoint = getSampleForCharacteristic(characteristic, 100) and
confidence >= SharedCharacteristics::highConfidence() and
@@ -58,9 +58,9 @@ where
message = characteristic
select endpoint, message + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", //
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", //
+ signature, "signature", //
+ input, "input" //
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
index 0a70b376c82..49be5c1d45f 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractPositiveExamples.ql
@@ -13,8 +13,9 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
from
- Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta, string package,
- string type, boolean subtypes, string name, string signature, string input
+ Endpoint endpoint, SinkType sinkType, ApplicationModeMetadataExtractor meta,
+ DollarAtString package, DollarAtString type, DollarAtString subtypes, DollarAtString name,
+ DollarAtString signature, DollarAtString input
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
@@ -24,9 +25,9 @@ where
CharacteristicsImpl::isKnownSink(endpoint, sinkType)
select endpoint, sinkType + "\nrelated locations: $@." + "\nmetadata: $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, CallContext()), "CallContext", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", //
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", //
+ signature, "signature", //
+ input, "input" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
index 85e28436df3..1a94d1a6338 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeCharacteristics.qll
@@ -129,7 +129,7 @@ class FrameworkModeMetadataExtractor extends string {
}
predicate hasMetadata(
- Endpoint e, string package, string type, boolean subtypes, string name, string signature,
+ Endpoint e, string package, string type, string subtypes, string name, string signature,
string input, string parameterName
) {
exists(Callable callable, int paramIdx |
@@ -137,7 +137,7 @@ class FrameworkModeMetadataExtractor extends string {
input = AutomodelSharedUtil::getArgumentForIndex(paramIdx) and
package = callable.getDeclaringType().getPackage().getName() and
type = callable.getDeclaringType().getErasure().(RefType).nestedName() and
- subtypes = this.considerSubtypes(callable) and
+ subtypes = this.considerSubtypes(callable).toString() and
name = callable.getName() and
parameterName = e.asParameter().getName() and
signature = ExternalFlow::paramsString(callable)
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
index 7f7f050fac4..391105b9d56 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractCandidates.ql
@@ -16,8 +16,9 @@ private import AutomodelFrameworkModeCharacteristics
private import AutomodelSharedUtil
from
- Endpoint endpoint, string message, FrameworkModeMetadataExtractor meta, string package,
- string type, boolean subtypes, string name, string signature, string input, string parameterName
+ Endpoint endpoint, string message, FrameworkModeMetadataExtractor meta, DollarAtString package,
+ DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
+ DollarAtString input, DollarAtString parameterName
where
not exists(CharacteristicsImpl::UninterestingToModelCharacteristic u |
u.appliesToEndpoint(endpoint)
@@ -42,10 +43,10 @@ select endpoint,
message + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", //
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input", //
- parameterName.(DollarAtString), "parameterName" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", //
+ signature, "signature", //
+ input, "input", //
+ parameterName, "parameterName" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
index 9036d638c2b..fb4bbc0e675 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractNegativeExamples.ql
@@ -13,9 +13,10 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
from
- Endpoint endpoint, EndpointCharacteristic characteristic, float confidence, string message,
- FrameworkModeMetadataExtractor meta, string package, string type, boolean subtypes, string name,
- string signature, string input, string parameterName
+ Endpoint endpoint, EndpointCharacteristic characteristic, float confidence,
+ DollarAtString message, FrameworkModeMetadataExtractor meta, DollarAtString package,
+ DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
+ DollarAtString input, DollarAtString parameterName
where
characteristic.appliesToEndpoint(endpoint) and
confidence >= SharedCharacteristics::highConfidence() and
@@ -39,10 +40,10 @@ select endpoint,
message + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", //
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input", //
- parameterName.(DollarAtString), "parameterName" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", //
+ signature, "signature", //
+ input, "input", //
+ parameterName, "parameterName" //
diff --git a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
index eeaf7037a15..41cca592380 100644
--- a/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelFrameworkModeExtractPositiveExamples.ql
@@ -13,8 +13,9 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
from
- Endpoint endpoint, SinkType sinkType, FrameworkModeMetadataExtractor meta, string package,
- string type, boolean subtypes, string name, string signature, string input, string parameterName
+ Endpoint endpoint, SinkType sinkType, FrameworkModeMetadataExtractor meta, DollarAtString package,
+ DollarAtString type, DollarAtString subtypes, DollarAtString name, DollarAtString signature,
+ DollarAtString input, DollarAtString parameterName
where
// Exclude endpoints that have contradictory endpoint characteristics, because we only want examples we're highly
// certain about in the prompt.
@@ -26,10 +27,10 @@ select endpoint,
sinkType + "\nrelated locations: $@, $@." + "\nmetadata: $@, $@, $@, $@, $@, $@, $@.", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, MethodDoc()), "MethodDoc", //
CharacteristicsImpl::getRelatedLocationOrCandidate(endpoint, ClassDoc()), "ClassDoc", //
- package.(DollarAtString), "package", //
- type.(DollarAtString), "type", //
- subtypes.toString().(DollarAtString), "subtypes", //
- name.(DollarAtString), "name", //
- signature.(DollarAtString), "signature", //
- input.(DollarAtString), "input", //
- parameterName.(DollarAtString), "parameterName" //
+ package, "package", //
+ type, "type", //
+ subtypes, "subtypes", //
+ name, "name", //
+ signature, "signature", //
+ input, "input", //
+ parameterName, "parameterName" //
From 86559317d70eb3222a693457cd5e82acbb916995 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 10:57:03 +0200
Subject: [PATCH 199/813] Java: update comments
---
.../AutomodelApplicationModeCharacteristics.qll | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 882c957f3a5..2773e1f2cfc 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -32,9 +32,8 @@ private class ArgumentNode extends DataFlow::Node {
* A candidates implementation.
*
* Some important notes:
- * - This mode is using parameters as endpoints.
- * - Sink- and neutral-information is being used from MaD models.
- * - When available, we use method- and class-java-docs as related locations.
+ * - This mode is using arguments as endpoints.
+ * - We use the `CallContext` (the surrounding call expression) as related location.
*/
module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig {
// for documentation of the implementations here, see the QLDoc in the CandidateSig signature module.
@@ -112,7 +111,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
}
/**
- * Returns the callable that contains the given endpoint.
+ * Returns the API callable being modelled.
*
* Each Java mode should implement this predicate.
*/
@@ -279,8 +278,10 @@ private class ClassQualifierCharacteristic extends CharacteristicsImpl::NotASink
}
/**
- * A characteristic that limits candidates to parameters of methods that are recognized as `ModelApi`, iow., APIs that
- * are considered worth modeling.
+ * A call to a method that's known locally will not be considered as a candidate to model.
+ *
+ * The reason is that we would expect data/taint flow into the method implementation to uncover
+ * any sinks that are present there.
*/
private class ArgumentToLocalCall extends CharacteristicsImpl::UninterestingToModelCharacteristic {
ArgumentToLocalCall() { this = "argument to local call" }
From 12ea5e0e90cdc6d2414af2aff7505d7ddb5fec11 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 11:53:02 +0200
Subject: [PATCH 200/813] Java: fix sanitizer bug
---
.../src/Telemetry/AutomodelApplicationModeCharacteristics.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 2773e1f2cfc..6743eca3789 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -49,8 +49,8 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
// Sanitizers are currently not modeled in MaD. TODO: check if this has large negative impact.
predicate isSanitizer(Endpoint e, EndpointType t) {
+ exists(t) and
(
- exists(t) and
e.getType() instanceof BoxedType
or
e.getType() instanceof PrimitiveType
From a9811fe2c3c2fec4968f69e4a21ffa93ff213b6b Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 31 May 2023 10:51:42 +0100
Subject: [PATCH 201/813] Swift: Make Macro.getName() more efficient.
---
cpp/ql/lib/semmle/code/cpp/Macro.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cpp/ql/lib/semmle/code/cpp/Macro.qll b/cpp/ql/lib/semmle/code/cpp/Macro.qll
index 4378cec4857..bd916d4bc4e 100644
--- a/cpp/ql/lib/semmle/code/cpp/Macro.qll
+++ b/cpp/ql/lib/semmle/code/cpp/Macro.qll
@@ -34,7 +34,7 @@ class Macro extends PreprocessorDirective, @ppd_define {
* Gets the name of the macro. For example, `MAX` in
* `#define MAX(x,y) (((x)>(y))?(x):(y))`.
*/
- string getName() { result = this.getHead().splitAt("(", 0) }
+ string getName() { result = this.getHead().regexpCapture("([^(]*+).*", 1) }
/** Holds if the macro has name `name`. */
predicate hasName(string name) { this.getName() = name }
From ace7b6b7116bf5dea1cfcd3bc6cef3ca65feb83b Mon Sep 17 00:00:00 2001
From: Jeroen Ketema
Date: Wed, 31 May 2023 11:49:00 +0200
Subject: [PATCH 202/813] C++: Add `cpp/invalid-pointer-deref` FP test case
---
.../pointer-deref/InvalidPointerDeref.expected | 17 +++++++++++++++++
.../Security/CWE/CWE-193/pointer-deref/test.cpp | 14 ++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
index 6b4d039ee6b..09c75e7369c 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/InvalidPointerDeref.expected
@@ -723,6 +723,15 @@ edges
| test.cpp:359:16:359:27 | end_plus_one | test.cpp:358:14:358:26 | Load: * ... |
| test.cpp:359:16:359:27 | end_plus_one | test.cpp:359:14:359:32 | Load: * ... |
| test.cpp:359:16:359:31 | ... + ... | test.cpp:359:14:359:32 | Load: * ... |
+| test.cpp:363:14:363:27 | new[] | test.cpp:365:15:365:15 | p |
+| test.cpp:365:15:365:15 | p | test.cpp:368:5:368:10 | ... += ... |
+| test.cpp:365:15:365:15 | p | test.cpp:368:5:368:10 | ... += ... |
+| test.cpp:368:5:368:10 | ... += ... | test.cpp:371:7:371:7 | p |
+| test.cpp:368:5:368:10 | ... += ... | test.cpp:371:7:371:7 | p |
+| test.cpp:368:5:368:10 | ... += ... | test.cpp:372:16:372:16 | p |
+| test.cpp:368:5:368:10 | ... += ... | test.cpp:372:16:372:16 | p |
+| test.cpp:371:7:371:7 | p | test.cpp:372:15:372:16 | Load: * ... |
+| test.cpp:372:16:372:16 | p | test.cpp:372:15:372:16 | Load: * ... |
nodes
| test.cpp:4:15:4:20 | call to malloc | semmle.label | call to malloc |
| test.cpp:5:15:5:15 | p | semmle.label | p |
@@ -1050,6 +1059,13 @@ nodes
| test.cpp:359:14:359:32 | Load: * ... | semmle.label | Load: * ... |
| test.cpp:359:16:359:27 | end_plus_one | semmle.label | end_plus_one |
| test.cpp:359:16:359:31 | ... + ... | semmle.label | ... + ... |
+| test.cpp:363:14:363:27 | new[] | semmle.label | new[] |
+| test.cpp:365:15:365:15 | p | semmle.label | p |
+| test.cpp:368:5:368:10 | ... += ... | semmle.label | ... += ... |
+| test.cpp:368:5:368:10 | ... += ... | semmle.label | ... += ... |
+| test.cpp:371:7:371:7 | p | semmle.label | p |
+| test.cpp:372:15:372:16 | Load: * ... | semmle.label | Load: * ... |
+| test.cpp:372:16:372:16 | p | semmle.label | p |
subpaths
#select
| test.cpp:6:14:6:15 | Load: * ... | test.cpp:4:15:4:20 | call to malloc | test.cpp:6:14:6:15 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:4:15:4:20 | call to malloc | call to malloc | test.cpp:5:19:5:22 | size | size |
@@ -1077,3 +1093,4 @@ subpaths
| test.cpp:350:15:350:19 | Load: * ... | test.cpp:347:14:347:27 | new[] | test.cpp:350:15:350:19 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:347:14:347:27 | new[] | new[] | test.cpp:348:20:348:23 | size | size |
| test.cpp:358:14:358:26 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:358:14:358:26 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
| test.cpp:359:14:359:32 | Load: * ... | test.cpp:355:14:355:27 | new[] | test.cpp:359:14:359:32 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@ + 2. | test.cpp:355:14:355:27 | new[] | new[] | test.cpp:356:20:356:23 | size | size |
+| test.cpp:372:15:372:16 | Load: * ... | test.cpp:363:14:363:27 | new[] | test.cpp:372:15:372:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:363:14:363:27 | new[] | new[] | test.cpp:365:19:365:22 | size | size |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
index 3dfd8b89097..3711f272e76 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp
@@ -358,3 +358,17 @@ void test25(unsigned size) {
int val1 = *end_plus_one; // BAD
int val2 = *(end_plus_one + 1); // BAD
}
+
+void test26(unsigned size) {
+ char *xs = new char[size];
+ char *p = xs;
+ char *end = p + size;
+
+ if (p + 4 <= end) {
+ p += 4;
+ }
+
+ if (p < end) {
+ int val = *p; // GOOD [FALSE POSITIVE]
+ }
+}
From 5981ce4cb1499480ba59a203dda82094c51aac49 Mon Sep 17 00:00:00 2001
From: Arthur Baars
Date: Wed, 31 May 2023 12:15:21 +0200
Subject: [PATCH 203/813] Swift: accept test output from failed CFG consistency
queries
---
.../decl/enumdecl/CONSISTENCY/CfgConsistency.expected | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected
diff --git a/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected
new file mode 100644
index 00000000000..4de95c00602
--- /dev/null
+++ b/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected
@@ -0,0 +1,10 @@
+deadEnd
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
+| file://:0:0:0:0 | ... = ... |
From 282ee08ba9cf62db0b9fd911699bbedf4096ccb4 Mon Sep 17 00:00:00 2001
From: Tony Torralba
Date: Wed, 31 May 2023 13:26:35 +0200
Subject: [PATCH 204/813] Java: Fix GsonDeserializableField
---
.../semmle/code/java/frameworks/google/GsonSerializability.qll | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
index 1e41ad0c458..f7de80daaf4 100644
--- a/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/google/GsonSerializability.qll
@@ -50,8 +50,7 @@ private class GsonDeserializableField extends DeserializableField {
exists(GsonDeserializableType superType |
superType = this.getDeclaringType().getAnAncestor() and
not superType instanceof TypeObject and
- //superType.fromSource()
- not superType.(RefType).getPackage().getName().matches("java%")
+ superType.fromSource()
)
}
}
From 5a9d09c49e1b09c20343e568a54977957aa4a944 Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 13:36:58 +0200
Subject: [PATCH 205/813] Java: docs update
Co-authored-by: Aditya Sharad <6874315+adityasharad@users.noreply.github.com>
---
java/ql/src/Telemetry/AutomodelSharedUtil.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelSharedUtil.qll b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
index 2ca38506160..11161f599be 100644
--- a/java/ql/src/Telemetry/AutomodelSharedUtil.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
@@ -50,7 +50,7 @@ predicate isKnownKind(
type instanceof AutomodelEndpointTypes::CommandInjectionSinkType
}
-/** Gets the argument name for the argument with the index `index`. */
+/** Gets the models-as-data description for the method argument with the index `index`. */
bindingset[index]
string getArgumentForIndex(int index) {
index = -1 and result = "Argument[this]"
From ea5c36491b3581e40ea56ca1a946ad93eefeced8 Mon Sep 17 00:00:00 2001
From: Taus
Date: Wed, 31 May 2023 11:39:20 +0000
Subject: [PATCH 206/813] Java: Improve documentation of sampling strategy
---
...AutomodelApplicationModeExtractNegativeExamples.ql | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
index 7407be0be57..19beefad3d3 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeExtractNegativeExamples.ql
@@ -14,7 +14,10 @@ private import AutomodelEndpointTypes
private import AutomodelSharedUtil
/**
- * Gets a sample of endpoints for which the given characteristic applies.
+ * Gets a sample of endpoints (of at most `limit` samples) for which the given characteristic applies.
+ *
+ * The main purpose of this helper predicate is to avoid selecting too many samples, as this may
+ * cause the SARIF file to exceed the maximum size limit.
*/
bindingset[limit]
Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
@@ -28,7 +31,11 @@ Endpoint getSampleForCharacteristic(EndpointCharacteristic c, int limit) {
loc.getFile().getAbsolutePath(), loc.getStartLine(), loc.getStartColumn(),
loc.getEndLine(), loc.getEndColumn()
) and
- // we order the endpoints by location, but (to avoid bias) we select the indices semi-randomly
+ // To avoid selecting samples that are too close together (as the ranking above goes by file
+ // path first), we select `limit` evenly spaced samples from the ranked list of endpoints. By
+ // default this would always include the first sample, so we add a random-chosen prime offset
+ // to the first sample index, and reduce modulo the number of endpoints.
+ // Finally, we add 1 to the result, as ranking results in a 1-indexed relation.
n = 1 + (([0 .. limit - 1] * (num_endpoints / limit).floor() + 46337) % num_endpoints)
)
}
From daad2e1bd38756b3f6510f0939b6b0f4676d7707 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 31 May 2023 10:46:25 +0100
Subject: [PATCH 207/813] Swift: Use regexp for function name.
---
swift/ql/lib/codeql/swift/elements/decl/Function.qll | 10 ++++++++++
swift/ql/lib/codeql/swift/security/SensitiveExprs.qll | 2 +-
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/swift/ql/lib/codeql/swift/elements/decl/Function.qll b/swift/ql/lib/codeql/swift/elements/decl/Function.qll
index ad1fa957d94..7fc5b5b2155 100644
--- a/swift/ql/lib/codeql/swift/elements/decl/Function.qll
+++ b/swift/ql/lib/codeql/swift/elements/decl/Function.qll
@@ -6,6 +6,16 @@ private import codeql.swift.elements.decl.Method
*/
class Function extends Generated::Function, Callable {
override string toString() { result = this.getName() }
+
+ /**
+ * Gets the name of this function, without the argument list. For example
+ * a function with name `myFunction(arg:)` has short name `myFunction`.
+ */
+ string getShortName() {
+ // match as many characters as possible that are not `(`.
+ // (`*+` is possessive matching)
+ result = this.getName().regexpCapture("([^(]*+).*", 1)
+ }
}
/**
diff --git a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
index d750e673121..a991f8e2c37 100644
--- a/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
+++ b/swift/ql/lib/codeql/swift/security/SensitiveExprs.qll
@@ -102,7 +102,7 @@ private class SensitiveFunction extends Function {
string name; // name of the function, not including the argument list.
SensitiveFunction() {
- name = this.getName().splitAt("(", 0) and
+ name = this.getShortName() and
name.regexpMatch(sensitiveType.getRegexp())
}
From caf250cc1b18bc4000b3a2dd8353664c61b33450 Mon Sep 17 00:00:00 2001
From: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
Date: Wed, 31 May 2023 12:54:42 +0100
Subject: [PATCH 208/813] Swift: Update the QLdoc on Callable.
---
swift/ql/.generated.list | 4 ++--
swift/ql/lib/codeql/swift/generated/Callable.qll | 2 ++
swift/ql/lib/codeql/swift/generated/Raw.qll | 2 ++
swift/schema.py | 3 ++-
4 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/swift/ql/.generated.list b/swift/ql/.generated.list
index 53847acba54..523796c28ad 100644
--- a/swift/ql/.generated.list
+++ b/swift/ql/.generated.list
@@ -369,7 +369,7 @@ lib/codeql/swift/elements.qll 3df0060edd2b2030f4e4d7d5518afe0073d798474d9b1d6185
lib/codeql/swift/generated/AstNode.qll 02ca56d82801f942ae6265c6079d92ccafdf6b532f6bcebd98a04029ddf696e4 6216fda240e45bd4302fa0cf0f08f5f945418b144659264cdda84622b0420aa2
lib/codeql/swift/generated/AvailabilityInfo.qll c648a66cf45414c85cf9cc69aa05b765a49d0c18cd9c101c34f99a9adc38a1ee 54ba7b07b4177d35e85d19363aa7adcda29cda185a5818e5fcb7c678c093e0ba
lib/codeql/swift/generated/AvailabilitySpec.qll fb1255f91bb5e41ad4e9c675a2efbc50d0fb366ea2de68ab7eebd177b0795309 144e0c2e7d6c62ecee43325f7f26dcf437881edf0b75cc1bc898c6c4b61fdeaf
-lib/codeql/swift/generated/Callable.qll 9dcf09a2f227dd6f569f007a07fb368d6b928ffd002535bb97118361430d948c 5c203f5f6b4f8b6748e61e09bb46c55442a2fb36f2d1fa950e6f81bdda562709
+lib/codeql/swift/generated/Callable.qll aaa3a8fbf04cb1be4c99de917353dd3a56ce1bce97af745e4c922957b7480e44 1896c0fb1690aef99accdaba0fd906549f5a971d9f663945e4ca6d6e2a21e2e4
lib/codeql/swift/generated/Comment.qll f58b49f6e68c21f87c51e2ff84c8a64b09286d733e86f70d67d3a98fe6260bd6 975bbb599a2a7adc35179f6ae06d9cbc56ea8a03b972ef2ee87604834bc6deb1
lib/codeql/swift/generated/DbFile.qll a49b2a2cb2788cb49c861ebcd458b8daead7b15adb19c3a9f4db3bf39a0051fc a49b2a2cb2788cb49c861ebcd458b8daead7b15adb19c3a9f4db3bf39a0051fc
lib/codeql/swift/generated/DbLocation.qll b9baea963d9fa82068986512c0649d1050897654eee3df51dba17cf6b1170873 b9baea963d9fa82068986512c0649d1050897654eee3df51dba17cf6b1170873
@@ -384,7 +384,7 @@ lib/codeql/swift/generated/OtherAvailabilitySpec.qll 0e26a203b26ff0581b7396b0c6d
lib/codeql/swift/generated/ParentChild.qll 01b27b48a12955a45ea26d0f7888a160faac9fd5fb57a19e87365318e9b21a30 88090ef26a7ce63f4ba88fa735e2c8207fd1de00076532083d93a7a02553797e
lib/codeql/swift/generated/PlatformVersionAvailabilitySpec.qll f82d9ca416fe8bd59b5531b65b1c74c9f317b3297a6101544a11339a1cffce38 7f5c6d3309e66c134107afe55bae76dfc9a72cb7cdd6d4c3706b6b34cee09fa0
lib/codeql/swift/generated/PureSynthConstructors.qll 173c0dd59396a1de26fe870e3bc2766c46de689da2a4d8807cb62023bbce1a98 173c0dd59396a1de26fe870e3bc2766c46de689da2a4d8807cb62023bbce1a98
-lib/codeql/swift/generated/Raw.qll 13cf09f9b2f628831b6b715448779366959a4c44b1b5ffc97397654fc8620486 03d60bdb6543d87a83ca50a3977c98c08d936d435981ae0b373f98ecde7a142b
+lib/codeql/swift/generated/Raw.qll 5715979160eac96fe4b54b73cdb88958617b9e3d66928c61029a12fab1c25902 5dbaaf2e03512107fc6e533d1388b7e6c8104ec5bcc43781256d67750d841ce8
lib/codeql/swift/generated/Synth.qll 551fdf7e4b53f9ee1314d1bb42c2638cf82f45bfa1f40a635dfa7b6072e4418c 9ab178464700a19951fc5285acacda4913addee81515d8e072b3d7055935a814
lib/codeql/swift/generated/SynthConstructors.qll 2f801bd8b0db829b0253cd459ed3253c1fdfc55dce68ebc53e7fec138ef0aca4 2f801bd8b0db829b0253cd459ed3253c1fdfc55dce68ebc53e7fec138ef0aca4
lib/codeql/swift/generated/UnknownFile.qll 0fcf9beb8de79440bcdfff4bb6ab3dd139bd273e6c32754e05e6a632651e85f6 0fcf9beb8de79440bcdfff4bb6ab3dd139bd273e6c32754e05e6a632651e85f6
diff --git a/swift/ql/lib/codeql/swift/generated/Callable.qll b/swift/ql/lib/codeql/swift/generated/Callable.qll
index db03af1dbd0..2cfcdeb5c11 100644
--- a/swift/ql/lib/codeql/swift/generated/Callable.qll
+++ b/swift/ql/lib/codeql/swift/generated/Callable.qll
@@ -10,6 +10,8 @@ module Generated {
class Callable extends Synth::TCallable, Element {
/**
* Gets the name of this callable, if it exists.
+ *
+ * The name includes any arguments of the callable, for example `myFunction(arg:)`.
*/
string getName() { result = Synth::convertCallableToRaw(this).(Raw::Callable).getName() }
diff --git a/swift/ql/lib/codeql/swift/generated/Raw.qll b/swift/ql/lib/codeql/swift/generated/Raw.qll
index 8ebecc81a15..05e46d484b3 100644
--- a/swift/ql/lib/codeql/swift/generated/Raw.qll
+++ b/swift/ql/lib/codeql/swift/generated/Raw.qll
@@ -21,6 +21,8 @@ module Raw {
class Callable extends @callable, Element {
/**
* Gets the name of this callable, if it exists.
+ *
+ * The name includes any arguments of the callable, for example `myFunction(arg:)`.
*/
string getName() { callable_names(this, result) }
diff --git a/swift/schema.py b/swift/schema.py
index ef2c357899f..0ca1e25a6a3 100644
--- a/swift/schema.py
+++ b/swift/schema.py
@@ -228,7 +228,8 @@ class ParamDecl(VarDecl):
""")
class Callable(Element):
- name: optional[string] | doc("name of this callable")
+ name: optional[string] | doc("name of this callable") | desc("The name includes any arguments "
+ "of the callable, for example `myFunction(arg:)`.")
self_param: optional[ParamDecl] | child
params: list[ParamDecl] | child
body: optional["BraceStmt"] | child | desc("The body is absent within protocol declarations.")
From 03051dde7fae649a5c67301cb5476daca3cc502a Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 12:40:21 +0200
Subject: [PATCH 209/813] Java: spelling
---
.../src/Telemetry/AutomodelApplicationModeCharacteristics.qll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
index 6743eca3789..bf8a0ac1274 100644
--- a/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
+++ b/java/ql/src/Telemetry/AutomodelApplicationModeCharacteristics.qll
@@ -111,7 +111,7 @@ module ApplicationCandidatesImpl implements SharedCharacteristics::CandidateSig
}
/**
- * Returns the API callable being modelled.
+ * Returns the API callable being modeled.
*
* Each Java mode should implement this predicate.
*/
From 5de56db3af741d1ce8104f9d9ddffa9e5f4fcf8f Mon Sep 17 00:00:00 2001
From: Stephan Brandauer
Date: Wed, 31 May 2023 14:10:54 +0200
Subject: [PATCH 210/813] Java: QlDoc for isKnownKind
---
java/ql/src/Telemetry/AutomodelSharedUtil.qll | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/java/ql/src/Telemetry/AutomodelSharedUtil.qll b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
index 11161f599be..7b4a88c8243 100644
--- a/java/ql/src/Telemetry/AutomodelSharedUtil.qll
+++ b/java/ql/src/Telemetry/AutomodelSharedUtil.qll
@@ -22,6 +22,10 @@ class DollarAtString extends string {
}
}
+/**
+ * Holds for all combinations of MaD kinds (`kind`) and their human readable
+ * descriptions.
+ */
predicate isKnownKind(
string kind, string humanReadableKind, AutomodelEndpointTypes::EndpointType type
) {
From 43d6bf04b5c07a0fe8214bfa02b0383b09a6f703 Mon Sep 17 00:00:00 2001
From: Michael Nebel
Date: Fri, 26 May 2023 11:32:12 +0200
Subject: [PATCH 211/813] C#: Make synthetic implicit casts when values are
provided using the DefaultParameterValue attribute.
---
.../Entities/Expression.cs | 5 ++
.../Entities/Expressions/ImplicitCast.cs | 74 +++++++++++++++----
.../SymbolExtensions.cs | 2 +-
3 files changed, 64 insertions(+), 17 deletions(-)
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
index 698be2e2c35..a77c0b30095 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs
@@ -211,6 +211,11 @@ namespace Semmle.Extraction.CSharp.Entities
return Default.CreateGenerated(cx, parent, childIndex, location, ValueAsString(null));
}
+ if (type.SpecialType is SpecialType.None)
+ {
+ return ImplicitCast.CreateGenerated(cx, parent, childIndex, type, defaultValue, location);
+ }
+
if (type.SpecialType is SpecialType.System_DateTime)
{
return DateTimeObjectCreation.CreateGenerated(cx, parent, childIndex, type, defaultValue, location);
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
index 2d617cdb1b9..20daedc0ae8 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs
@@ -1,3 +1,4 @@
+using System.Linq;
using Microsoft.CodeAnalysis;
using Semmle.Extraction.Kinds;
@@ -11,33 +12,74 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
private set;
}
- public ImplicitCast(ExpressionNodeInfo info)
+ private ImplicitCast(ExpressionNodeInfo info)
: base(new ExpressionInfo(info.Context, info.ConvertedType, info.Location, ExprKind.CAST, info.Parent, info.Child, true, info.ExprValue))
{
Expr = Factory.Create(new ExpressionNodeInfo(Context, info.Node, this, 0));
}
- public ImplicitCast(ExpressionNodeInfo info, IMethodSymbol method)
+ private ImplicitCast(ExpressionNodeInfo info, IMethodSymbol method)
: base(new ExpressionInfo(info.Context, info.ConvertedType, info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue))
{
Expr = Factory.Create(info.SetParent(this, 0));
- var target = Method.Create(Context, method);
- if (target is not null)
- Context.TrapWriter.Writer.expr_call(this, target);
- else
- Context.ModelError(info.Node, "Failed to resolve target for operator invocation");
+ AddOperatorCall(method);
}
- ///
- /// Creates a new expression, adding casts as required.
- ///
- /// The extraction context.
- /// The expression node.
- /// The parent of the expression.
- /// The child number.
- /// A type hint.
- /// A new expression.
+ private ImplicitCast(ExpressionInfo info, IMethodSymbol method, object value) : base(info)
+ {
+ Expr = Literal.CreateGenerated(Context, this, 0, method.Parameters[0].Type, value, info.Location);
+
+ AddOperatorCall(method);
+ }
+
+ private void AddOperatorCall(IMethodSymbol method)
+ {
+ var target = Method.Create(Context, method);
+ Context.TrapWriter.Writer.expr_call(this, target);
+ }
+
+ private static IMethodSymbol? GetImplicitConversionMethod(ITypeSymbol type, object value) =>
+ type
+ .GetMembers()
+ .Where(m =>
+ m is IMethodSymbol method &&
+ method.GetName() == "op_Implicit" &&
+ method.Parameters.Length == 1 &&
+ method.Parameters[0].Type.Name == value.GetType().Name
+ )
+ .Cast()
+ .FirstOrDefault();
+
+ // Creates a new generated expression with an implicit cast added, if needed.
+ public static Expression CreateGenerated(Context cx, IExpressionParentEntity parent, int childIndex, ITypeSymbol type, object value,
+ Extraction.Entities.Location location)
+ {
+ ExpressionInfo create(ExprKind kind, string? v) =>
+ new ExpressionInfo(
+ cx,
+ AnnotatedTypeSymbol.CreateNotAnnotated(type),
+ location,
+ kind,
+ parent,
+ childIndex,
+ true,
+ v);
+
+ var method = GetImplicitConversionMethod(type, value);
+ if (method is not null)
+ {
+ var info = create(ExprKind.OPERATOR_INVOCATION, null);
+ return new ImplicitCast(info, method, value);
+ }
+ else
+ {
+ cx.ModelError(location, "Failed to resolve target for implicit operator invocation for a parameter default.");
+ return new Expression(create(ExprKind.UNKNOWN, ValueAsString(value)));
+ }
+ }
+
+ // Creates a new expression, adding casts as required.
public static Expression Create(ExpressionNodeInfo info)
{
var resolvedType = info.ResolvedType;
diff --git a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs
index cd182fe4640..aaef1702532 100644
--- a/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs
+++ b/csharp/extractor/Semmle.Extraction.CSharp/SymbolExtensions.cs
@@ -25,7 +25,7 @@ namespace Semmle.Extraction.CSharp
Nullability = nullability;
}
- public static AnnotatedTypeSymbol? CreateNotAnnotated(ITypeSymbol symbol) =>
+ public static AnnotatedTypeSymbol? CreateNotAnnotated(ITypeSymbol? symbol) =>
symbol is null ? (AnnotatedTypeSymbol?)null : new AnnotatedTypeSymbol(symbol, NullableAnnotation.None);
}
From 83a8e3bdbc1883f42ecc2a050d15315565c74824 Mon Sep 17 00:00:00 2001
From: Michael Nebel
Date: Fri, 26 May 2023 11:32:34 +0200
Subject: [PATCH 212/813] C#: Add some more testcases.
---
.../library-tests/parameters/Parameters.cs | 9 ++++++-
.../library-tests/parameters/Parameters.cs_ | 9 ++++++-
.../library-tests/parameters/Parameters.dll | Bin 6144 -> 6144 bytes
.../parameters/Parameters.expected | 13 +++++++++++
.../library-tests/parameters/Parameters.ql | 22 +++++++++++++-----
5 files changed, 45 insertions(+), 8 deletions(-)
diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs b/csharp/ql/test/library-tests/parameters/Parameters.cs
index ebe17322bad..ee62454b404 100644
--- a/csharp/ql/test/library-tests/parameters/Parameters.cs
+++ b/csharp/ql/test/library-tests/parameters/Parameters.cs
@@ -25,7 +25,14 @@ public class Parameters
public void M17([Optional, DefaultParameterValue(null)] object arg7) => throw null;
public void M18([Optional, DefaultParameterValue(3)] int? arg8) => throw null;
public void M19([Optional, DecimalConstant(1, 0, 0, 0, 103)] decimal arg9) => throw null;
+ public void M20([Optional, DefaultParameterValue(7)] MyStruct arg10) => throw null;
+ public void M21([Optional, DefaultParameterValue("mystring")] MyStruct arg10) => throw null;
- public struct MyStruct { }
+ public struct MyStruct
+ {
+ public static implicit operator MyStruct(int i) => new MyStruct();
+ public static implicit operator MyStruct(string s) => new MyStruct();
+
+ }
public enum MyEnum { A = 1, B = 2 }
}
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/parameters/Parameters.cs_ b/csharp/ql/test/library-tests/parameters/Parameters.cs_
index 8fce6f198c3..136e7262b98 100644
--- a/csharp/ql/test/library-tests/parameters/Parameters.cs_
+++ b/csharp/ql/test/library-tests/parameters/Parameters.cs_
@@ -25,7 +25,14 @@ public class ParametersDll
public void M17([Optional, DefaultParameterValue(null)] object arg7) => throw null;
public void M18([Optional, DefaultParameterValue(3)] int? arg8) => throw null;
public void M19([Optional, DecimalConstant(1, 0, 0, 0, 103)] decimal arg9) => throw null;
+ public void M20([Optional, DefaultParameterValue(7)] MyStruct arg10) => throw null;
+ public void M21([Optional, DefaultParameterValue("mystring")] MyStruct arg10) => throw null;
- public struct MyStruct { }
+ public struct MyStruct
+ {
+ public static implicit operator MyStruct(int i) => new MyStruct();
+ public static implicit operator MyStruct(string s) => new MyStruct();
+
+ }
public enum MyEnum { A = 1, B = 2 }
}
\ No newline at end of file
diff --git a/csharp/ql/test/library-tests/parameters/Parameters.dll b/csharp/ql/test/library-tests/parameters/Parameters.dll
index b40c91369cbf75bb1c1296e3a7652ad3a7abc331..3358306fc8976442d42361e9615275591f8a928d 100644
GIT binary patch
delta 2032
zcmZ{ld2Ccg9LK*iZ})Y3uzg!>>9$*J*+MNy+XYDxIV?wE15H{BXoFzdl{AHxqa~0c
z^2(tB6heJU#6S=#S|M_XTIC2B&{Q!o0Tl#8AgF(MVS-mQA%16=R5Wpt`ONS8o#XxH
z&FtHbvW~K?8+PlP*RrrSVgu3D
zzy`Lx7USAwtxby>+tgF5xtfn{bXF~9GImrvbyj>b+sCPwNa~5riyNp_p%f4Al-?XG
zFEfvNFsqsqVa;b1-bO3W{Yf%G78}2bv6hYJrQ#4#Y^CE-W?&;Bj!PR)$PoRLLUEPm
zcFip9&(QHrsJY$pFOJ`8DF%h&ZXI8Micf0hXqL*cE+@qGBQNS{K0WkJ#ww{j3Vef1
zQ8>7Uc576h8pCvzQrmRUz%@oO<_n^_qWdhKkl>cDa!3VxY^1QSo124j2)QWu^+q0H^}OImC)1KQKl_W*QdIuTKiNt)!II#`kw1k
zYBPIoD-6H^mvXO>_u&8&={Bm8Dhd0IdIWFtHXSgkktz*`jarUl2;wcH*5U(%&|}mV
zoJSUp8ub#@0Gu#NP`TMSWn?;23FEX;Ww?YKd}!1}TtzNEGHNFKBKX9p1pD&vsZp&Q
zHxTEHT8-~;H$FFN6K)_M7meD6KTzP}vXQB{g+aJtRDY@{uH9Dpbr<3Xt+oXYTRu?;
zUjbN@N)hU63%o~4z6-#@3FA19avVoFj-$r>+i~x-{)yFc!}ZV!dUB0kZm@RBwG$_O
zm_z!pfD|}p{g?M>e~jh~UPOU;nl0LCS9I}|7M;3dr;h!GDv2-D^~NqM8Bt5{T-C)3
zlPM@B9mF*6*R0TtYu0GiY1WfA+R04pCUfyRnU5yYwXuecjR5XKIWmyRpGpAP7|Saa
zMkV7s?HAAw@*9$%Q_MJn??;V{>3EdpIL!*}$2F@oYq&xHv(U_0b$ZNP&3aV0?3j}b+f3z@=Pm(zKYae(P9B{#z5<2PeHiZB%OWC>E?h!XTin9Rj!GKvav7+i8BHj@wF
zMRF4MlGC+cOOE8)jdBtXuN>|qKCYa*QX*mE1dfY!+F7Tab?mt=y0p`!oh~(2p5}#p
zN1Wzzhs1TphlC{*Pid~V79u8Jw$zwc7>}2QzBWwP2MYeQi{a})15fwPa)O_uq&tif
z^lFvFuUCLom0TPovw1M8_3;OGkoSIy>Z*#Bp}w_|tQx@@i?KbDn*6>LP2SnmOT!i~
zc`V+tq`4{4v<&raZSz|in%mVpmJ9Dz_M8=~sy3W&TPb!Vukp^L75jZjA{6m{OsmQC
zsjQq99!(pTp_RwA2#wyjehV`E`GSM^sSQN}nnCZiey{rc{L*>TgDbq=;Dce#9r0D-
z97I($_s>%VCYP`}-oW%MyO&|F$Y`lI-1+rLFyl{4PM&ZilyyUNmL>fS
D+qg<0
delta 1875
zcmZ{leN2^A9LK-sdG5Vjke3IpAaCjg3_{_Kmxu~;A`y|Yfe6CT3egb*ti&Q6aW7tH
zI4Py4Kbp=NVJ%-G8Eh4;rDIucHd!w#r?wWkx#ia8YAf4X-}B>vwzi(_e9rIt{hf2}
zdCob{xuJqk!I42%?^yen!$avtV_^7nqcUx!a@?+bV3VC;*{Uh~SgL+-pGux>3L#Z|&2O^Uw?y6$2Rt%F_ye#|)q_AYL
z=&@3PAeJ-Gjphpn0p}qa_`%S=?s`k*nI0T4G?P_|8HWvQ=0SrRGd$=v6#a3fxl?#Z
z=oNpB#5bY)9yNYp`;eGY(Rh)dA%xG-ACrDcaQ;TO{FH{nTAPwI6IPLxwr7QRM@
zS+bz=I9;vunpoJxF-jP7HK}GKXL5H`kP9)xHswrt5XIyhe5*>TROnZ~!`xO;*=RBM
zA{(cSe)3Oa03OdM2GAHfj{W2bvevIBbRgNsX)81@s6nb-3+ffBeU1wHOr3mfA*tWy
ztvQ8A*i>&jDx1oMcOCTrRRqpB>Tw+9i5hcMGgUOk9rZNcL@Xv9wHv1qj}IIb3gSba
zk|{?HQ7yqIj#5;~IPWMf77x#~qcZU+SLmXn?!#wD!zD*O%3L}wJF0=XrMTj#?QFLU
zUpeYo1h4P_e(T6Sd;>qechn$m@T6XwQ?jI)(7SzxH1Hm2;(f=lEbUm9b}UP``G4o%
zEJxGlUpFn3&J{A;5^-|HDI;B|A>G(QDx5I?%T)36gll-76l#S}iqk3V6^6w>%#HP#
zire5bgB~ub&x}GYX>l5w4+_hL)xtVqv#^!)U=Nvy5i$*@NIyEn9}s%67)3}xB3ARu
zPev(EKnf4Jms_Fz4EnLS2TufPH627I{#Mf*
zF05u5hn1-`&kOgPop2c^$#`Rhs|U+?8}L%#hvy2-MHJ36!h0iPUa-!^l&A>aD_$1;
zq;bRab-Yw+7tdbY2+wk*&eaqauuOaudBL#zqOUG*?djRky{&7H&heSt*gX`!c(7_}
z`wO>H{>b~>4#ni!V=;AhVr++fB=#M9b=>oAx4u2>b8+QGKV`M@nyIPfgn2IkP5iHF
ziqiwlJ^C$tEIBOKnY$imXIq(8e(27H!+n2MANzP}Z`F;fFWXI4Z_&JY%`rK4%=(FA
zuim_W?dA<
Date: Wed, 31 May 2023 13:08:20 +0200
Subject: [PATCH 213/813] add an example of using dollar eq
---
.../Security/CWE-089/SqlInjection.inc.qhelp | 12 +++++++++--
.../CWE-089/examples/NoSqlInjectionFix.js | 8 ++-----
.../CWE-089/examples/NoSqlInjectionFix2.js | 21 +++++++++++++++++++
3 files changed, 33 insertions(+), 8 deletions(-)
create mode 100644 javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix2.js
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
index 1d99428b718..a30569aaf2c 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
@@ -69,8 +69,16 @@ object, so this code is vulnerable to a NoSQL injection attack.
-To fix this vulnerability, we can check that the user input is a
-literal value and not a query object before using it in a query.
+To fix this vulnerability we can use the $eq operator
+to ensure that the user input is interpreted as a literal value
+and not as a query object:
+
+
+
+
+
+Alternatively check that the user input is a
+literal value and not a query object before using it:
diff --git a/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
index fe982168be1..83f7c255618 100644
--- a/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
+++ b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix.js
@@ -11,11 +11,7 @@ app.use(express.urlencoded({ extended: false }));
app.delete("/api/delete", async (req, res) => {
let id = req.body.id;
- if (typeof id !== "string") {
- res.status(400).json({ status: "error" });
- return;
- }
- await Todo.deleteOne({ _id: id }); // GOOD: id is guaranteed to be a string
+ await Todo.deleteOne({ _id: { $eq: id } }); // GOOD: using $eq operator for the comparison
res.json({ status: "ok" });
-});
+});
\ No newline at end of file
diff --git a/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix2.js b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix2.js
new file mode 100644
index 00000000000..fe982168be1
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-089/examples/NoSqlInjectionFix2.js
@@ -0,0 +1,21 @@
+const express = require("express");
+const mongoose = require("mongoose");
+const Todo = mongoose.model(
+ "Todo",
+ new mongoose.Schema({ text: { type: String } }, { timestamps: true })
+);
+
+const app = express();
+app.use(express.json());
+app.use(express.urlencoded({ extended: false }));
+
+app.delete("/api/delete", async (req, res) => {
+ let id = req.body.id;
+ if (typeof id !== "string") {
+ res.status(400).json({ status: "error" });
+ return;
+ }
+ await Todo.deleteOne({ _id: id }); // GOOD: id is guaranteed to be a string
+
+ res.json({ status: "ok" });
+});
From 98820780af90934baf54335aa428478c694c7496 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Wed, 31 May 2023 13:51:22 +0200
Subject: [PATCH 214/813] show how to use mysql.escape in the sql-injection
qhelp
---
.../src/Security/CWE-089/SqlInjection.inc.qhelp | 6 ++++++
.../Security/CWE-089/examples/SqlInjectionFix2.js | 15 +++++++++++++++
2 files changed, 21 insertions(+)
create mode 100644 javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix2.js
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
index a30569aaf2c..cdf090e6914 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
@@ -55,6 +55,12 @@ immune to injection attacks.
+
+
+Alternatively, we can use a library like sqlstring to
+escape the user input before embedding it into the query string:
+
+
diff --git a/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix2.js b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix2.js
new file mode 100644
index 00000000000..843426cf732
--- /dev/null
+++ b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix2.js
@@ -0,0 +1,15 @@
+const app = require("express")(),
+ pg = require("pg"),
+ SqlString = require('sqlstring'),
+ pool = new pg.Pool(config);
+
+app.get("search", function handler(req, res) {
+ // GOOD: the category is escaped using mysql.escape
+ var query1 =
+ "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY='" +
+ SqlString.escape(req.params.category) +
+ "' ORDER BY PRICE";
+ pool.query(query1, [], function(err, results) {
+ // process results
+ });
+});
From 1e081058637d99a48f6714be43d891742995ee5c Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Wed, 31 May 2023 13:51:38 +0200
Subject: [PATCH 215/813] less duplicated headers in the sql-injection samples
---
.../ql/src/Security/CWE-089/SqlInjection.inc.qhelp | 4 ++--
.../Security/CWE-089/examples/NoSqlInjectionFix.js | 11 -----------
.../Security/CWE-089/examples/NoSqlInjectionFix2.js | 11 -----------
3 files changed, 2 insertions(+), 24 deletions(-)
diff --git a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
index cdf090e6914..cdda5100ba3 100644
--- a/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
+++ b/javascript/ql/src/Security/CWE-089/SqlInjection.inc.qhelp
@@ -80,14 +80,14 @@ to ensure that the user input is interpreted as a literal value
and not as a query object:
-
+
Alternatively check that the user input is a
literal value and not a query object before using it:
The handler constructs constructs an SQL query string from user input
and executes it as a database query using the pg library.
-THe user input may contain quote characters, so this code is vulnerable
+The user input may contain quote characters, so this code is vulnerable
to a SQL injection attack.
diff --git a/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
index 4391b83e391..dbe5c4e369a 100644
--- a/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
+++ b/javascript/ql/src/Security/CWE-089/examples/SqlInjectionFix.js
@@ -5,7 +5,7 @@ const app = require("express")(),
app.get("search", function handler(req, res) {
// GOOD: use parameters
var query2 =
- "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1" + " ORDER BY PRICE";
+ "SELECT ITEM,PRICE FROM PRODUCT WHERE ITEM_CATEGORY=$1 ORDER BY PRICE";
pool.query(query2, [req.params.category], function(err, results) {
// process results
});
From 606d601923897f423b6fe7bd5c1590935c649d58 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Thu, 1 Jun 2023 16:26:05 +0100
Subject: [PATCH 276/813] qlformat
---
ruby/ql/lib/codeql/ruby/Concepts.qll | 22 +++++++++----------
.../ql/lib/codeql/ruby/frameworks/Sqlite3.qll | 2 +-
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/ruby/ql/lib/codeql/ruby/Concepts.qll b/ruby/ql/lib/codeql/ruby/Concepts.qll
index 0a403734512..d74947482f8 100644
--- a/ruby/ql/lib/codeql/ruby/Concepts.qll
+++ b/ruby/ql/lib/codeql/ruby/Concepts.qll
@@ -78,18 +78,18 @@ module SqlExecution {
}
}
- /**
- * A data-flow node that performs SQL sanitization.
- */
- class SqlSanitization extends DataFlow::Node instanceof SqlSanitization::Range { }
+/**
+ * A data-flow node that performs SQL sanitization.
+ */
+class SqlSanitization extends DataFlow::Node instanceof SqlSanitization::Range { }
- /** Provides a class for modeling new SQL sanitization APIs. */
- module SqlSanitization {
- /**
- * A data-flow node that performs SQL sanitization.
- */
- abstract class Range extends DataFlow::Node { }
- }
+/** Provides a class for modeling new SQL sanitization APIs. */
+module SqlSanitization {
+ /**
+ * A data-flow node that performs SQL sanitization.
+ */
+ abstract class Range extends DataFlow::Node { }
+}
/**
* A data-flow node that executes a regular expression.
diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll
index 8a07e211a21..70744d6fcc8 100644
--- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll
+++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll
@@ -99,4 +99,4 @@ module Sqlite3 {
input = "Argument[0]" and output = "ReturnValue" and preservesValue = false
}
}
-}
\ No newline at end of file
+}
From 6fa9e13a2e76a15fbed57180fe49c9cd8f3fe1c4 Mon Sep 17 00:00:00 2001
From: Alex Ford
Date: Thu, 1 Jun 2023 16:27:20 +0100
Subject: [PATCH 277/813] Ruby: update TaintStep output
---
ruby/ql/test/library-tests/dataflow/local/TaintStep.expected | 3 +++
1 file changed, 3 insertions(+)
diff --git a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
index 2bfe1e9a9ab..e75c17e818e 100644
--- a/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
+++ b/ruby/ql/test/library-tests/dataflow/local/TaintStep.expected
@@ -2814,7 +2814,10 @@
| file://:0:0:0:0 | parameter position 0 of File.realdirpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realdirpath |
| file://:0:0:0:0 | parameter position 0 of File.realpath | file://:0:0:0:0 | [summary] to write: return (return) in File.realpath |
| file://:0:0:0:0 | parameter position 0 of Hash[] | file://:0:0:0:0 | [summary] read: argument position 0.any element in Hash[] |
+| file://:0:0:0:0 | parameter position 0 of Mysql2::Client.escape() | file://:0:0:0:0 | [summary] to write: return (return) in Mysql2::Client.escape() |
+| file://:0:0:0:0 | parameter position 0 of Mysql2::Client.new() | file://:0:0:0:0 | [summary] to write: return (return) in Mysql2::Client.new() |
| file://:0:0:0:0 | parameter position 0 of PG.new() | file://:0:0:0:0 | [summary] to write: return (return) in PG.new() |
+| file://:0:0:0:0 | parameter position 0 of SQLite3::Database.quote() | file://:0:0:0:0 | [summary] to write: return (return) in SQLite3::Database.quote() |
| file://:0:0:0:0 | parameter position 0 of Sequel.connect | file://:0:0:0:0 | [summary] to write: return (return) in Sequel.connect |
| file://:0:0:0:0 | parameter position 0 of String.try_convert | file://:0:0:0:0 | [summary] to write: return (return) in String.try_convert |
| file://:0:0:0:0 | parameter position 0 of \| | file://:0:0:0:0 | [summary] read: argument position 0.any element in \| |
From df4d156a36489ab02f15eb8a2c1ae07dee097ad4 Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 1 Jun 2023 11:28:12 -0400
Subject: [PATCH 278/813] C++: remove unneeded exists variables
---
.../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
index 0f8609c3c43..646044bc4f9 100644
--- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
+++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql
@@ -146,12 +146,12 @@ module ArrayAddressToDerefConfig implements DataFlow::StateConfigSig {
predicate isAdditionalFlowStep(
DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
) {
- exists(PointerArithmeticInstruction pai, Variable v, int size, int delta |
+ exists(PointerArithmeticInstruction pai, Variable v |
state1 = TArray(v) and
state2 = TOverflowArithmetic(pai) and
pai.getLeft() = node1.asInstruction() and
node2.asInstruction() = pai and
- pointerArithOverflow(pai, v, size, _, delta)
+ pointerArithOverflow(pai, v, _, _, _)
)
}
}
From 7290e2bfd9cb2e469ef152864588530801ff1192 Mon Sep 17 00:00:00 2001
From: Nick Rolfe
Date: Thu, 1 Jun 2023 17:06:34 +0100
Subject: [PATCH 279/813] Java: avoid call to Location.toString()
---
.../Implementation Hiding/ExposeRepresentation.ql | 2 +-
.../ExposeRepresentation.expected | 14 +++++++-------
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql b/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql
index 9f24744fa0c..2889de0b5cf 100644
--- a/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql
+++ b/java/ql/src/Violations of Best Practice/Implementation Hiding/ExposeRepresentation.ql
@@ -128,4 +128,4 @@ where
not exists(Property p | p.getBackingField() = f)
select c,
c.getName() + " exposes the internal representation stored in field " + f.getName() +
- ". The value may be modified $@.", why.getLocation(), whyText
+ ". The value may be modified $@.", why, whyText
diff --git a/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected
index 0056c25bb53..3162056ab42 100644
--- a/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected
+++ b/java/ql/test/query-tests/ExposeRepresentation/ExposeRepresentation.expected
@@ -1,7 +1,7 @@
-| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:5:5:5:19 | User.java:5:5:5:19 | after this call to getStrings |
-| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:13:12:13:26 | User.java:13:12:13:26 | after this call to getStrings |
-| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:38:12:38:26 | User.java:38:12:38:26 | after this call to getStrings |
-| ExposesRep.java:13:30:13:41 | getStringMap | getStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:9:5:9:21 | User.java:9:5:9:21 | after this call to getStringMap |
-| ExposesRep.java:17:15:17:24 | setStrings | setStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:22:5:22:6 | User.java:22:5:22:6 | through the variable ss |
-| ExposesRep.java:21:15:21:26 | setStringMap | setStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:27:5:27:5 | User.java:27:5:27:5 | through the variable m |
-| ExposesRep.java:29:14:29:21 | getArray | getArray exposes the internal representation stored in field array. The value may be modified $@. | User.java:31:5:31:18 | User.java:31:5:31:18 | after this call to getArray |
+| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:5:5:5:19 | getStrings(...) | after this call to getStrings |
+| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:13:12:13:26 | getStrings(...) | after this call to getStrings |
+| ExposesRep.java:11:19:11:28 | getStrings | getStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:38:12:38:26 | getStrings(...) | after this call to getStrings |
+| ExposesRep.java:13:30:13:41 | getStringMap | getStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:9:5:9:21 | getStringMap(...) | after this call to getStringMap |
+| ExposesRep.java:17:15:17:24 | setStrings | setStrings exposes the internal representation stored in field strings. The value may be modified $@. | User.java:22:5:22:6 | ss | through the variable ss |
+| ExposesRep.java:21:15:21:26 | setStringMap | setStringMap exposes the internal representation stored in field stringMap. The value may be modified $@. | User.java:27:5:27:5 | m | through the variable m |
+| ExposesRep.java:29:14:29:21 | getArray | getArray exposes the internal representation stored in field array. The value may be modified $@. | User.java:31:5:31:18 | getArray(...) | after this call to getArray |
From 6722892828f03977688a0f157854dfa6879d322c Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 12 May 2023 09:02:56 -0400
Subject: [PATCH 280/813] Java: switch 'android-widget' source kind to 'remote'
---
java/ql/lib/ext/android.widget.model.yml | 2 +-
java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 2 +-
java/ql/lib/semmle/code/java/frameworks/android/Widget.qll | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/java/ql/lib/ext/android.widget.model.yml b/java/ql/lib/ext/android.widget.model.yml
index ef4b015700a..aa6222c77d2 100644
--- a/java/ql/lib/ext/android.widget.model.yml
+++ b/java/ql/lib/ext/android.widget.model.yml
@@ -3,7 +3,7 @@ extensions:
pack: codeql/java-all
extensible: sourceModel
data:
- - ["android.widget", "EditText", True, "getText", "", "", "ReturnValue", "android-widget", "manual"]
+ - ["android.widget", "EditText", True, "getText", "", "", "ReturnValue", "remote", "manual"]
- addsTo:
pack: codeql/java-all
extensible: summaryModel
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index 4cb21496f5f..629b7140f19 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -286,7 +286,7 @@ module ModelValidation {
)
or
exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) |
- not kind = ["remote", "contentprovider", "android-widget", "android-external-storage-dir"] and
+ not kind = ["remote", "contentprovider", "android-external-storage-dir"] and
not kind.matches("qltest%") and
result = "Invalid kind \"" + kind + "\" in source model."
)
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
index 81c34179c15..506f11a9112 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
@@ -5,7 +5,7 @@ private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSources
private class DefaultAndroidWidgetSources extends RemoteFlowSource {
- DefaultAndroidWidgetSources() { sourceNode(this, "android-widget") }
+ DefaultAndroidWidgetSources() { sourceNode(this, "remote") }
override string getSourceType() { result = "Android widget source" }
}
From d035a29b4dcd99591463c1c5e54900069c05d302 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 12 May 2023 09:23:42 -0400
Subject: [PATCH 281/813] Java: update source kind documentation
---
.../customizing-library-models-for-java.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/codeql/codeql-language-guides/customizing-library-models-for-java.rst b/docs/codeql/codeql-language-guides/customizing-library-models-for-java.rst
index baa93e8eb0a..262a608f391 100644
--- a/docs/codeql/codeql-language-guides/customizing-library-models-for-java.rst
+++ b/docs/codeql/codeql-language-guides/customizing-library-models-for-java.rst
@@ -315,7 +315,7 @@ The following source kinds are supported:
Below is an enumeration of the remaining source kinds, but they are out of scope for this documentation:
-- **contentprovider**, **android-widget**, **android-external-storage-dir**.
+- **contentprovider**, **android-external-storage-dir**.
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
From 119b446dbcf0114d570caa458f6d707d9654df4f Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 12 May 2023 09:29:37 -0400
Subject: [PATCH 282/813] Java: add change note
---
.../2023-05-12-androidwidget-source-kind-to-remote.md | 4 ++++
1 file changed, 4 insertions(+)
create mode 100644 java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md
diff --git a/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md b/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md
new file mode 100644
index 00000000000..7a2714a6527
--- /dev/null
+++ b/java/ql/lib/change-notes/2023-05-12-androidwidget-source-kind-to-remote.md
@@ -0,0 +1,4 @@
+---
+category: minorAnalysis
+---
+* Changed the `android-widget` Java source kind to `remote`. Any custom data extensions that use the `android-widget` source kind will need to be updated accordingly in order to continue working.
From 5700a6eea4b9ffff98e929714e1f7ffafcf4cc5b Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 12 May 2023 09:48:50 -0400
Subject: [PATCH 283/813] Java: remove DefaultAndroidWidgetSources class
---
java/ql/lib/semmle/code/java/frameworks/android/Widget.qll | 6 ------
1 file changed, 6 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
index 506f11a9112..9cb39ed83a7 100644
--- a/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
+++ b/java/ql/lib/semmle/code/java/frameworks/android/Widget.qll
@@ -4,12 +4,6 @@ import java
private import semmle.code.java.dataflow.ExternalFlow
private import semmle.code.java.dataflow.FlowSources
-private class DefaultAndroidWidgetSources extends RemoteFlowSource {
- DefaultAndroidWidgetSources() { sourceNode(this, "remote") }
-
- override string getSourceType() { result = "Android widget source" }
-}
-
private class EditableToStringStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node n1, DataFlow::Node n2) {
exists(MethodAccess ma |
From de15013715b39f2c4e9c05680601bc2d09c81f43 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 12 May 2023 09:49:58 -0400
Subject: [PATCH 284/813] Java: remove RemoteFlowSources module
---
java/ql/lib/semmle/code/java/dataflow/FlowSources.qll | 7 -------
1 file changed, 7 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll
index e10cd0db708..d26aa5d35f6 100644
--- a/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/FlowSources.qll
@@ -36,13 +36,6 @@ abstract class RemoteFlowSource extends DataFlow::Node {
abstract string getSourceType();
}
-/**
- * A module for importing frameworks that define remote flow sources.
- */
-private module RemoteFlowSources {
- private import semmle.code.java.frameworks.android.Widget
-}
-
private class ExternalRemoteFlowSource extends RemoteFlowSource {
ExternalRemoteFlowSource() { sourceNode(this, "remote") }
From c9c93ca701b64095507b7be45494dd20145b668f Mon Sep 17 00:00:00 2001
From: Robert Marsh
Date: Thu, 1 Jun 2023 11:37:52 -0400
Subject: [PATCH 285/813] C++: test for strncmp false positives
---
.../ConstantSizeArrayOffByOne.expected | 10 ++++++++++
.../Security/CWE/CWE-193/constant-size/test.cpp | 16 +++++++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected
index 831280a015e..88e32ccf921 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/ConstantSizeArrayOffByOne.expected
@@ -12,6 +12,10 @@ edges
| test.cpp:79:27:79:34 | buf | test.cpp:70:33:70:33 | p |
| test.cpp:79:32:79:34 | buf | test.cpp:79:27:79:34 | buf |
| test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array |
+| test.cpp:134:25:134:27 | arr | test.cpp:136:9:136:16 | ... += ... |
+| test.cpp:136:9:136:16 | ... += ... | test.cpp:138:13:138:15 | arr |
+| test.cpp:143:18:143:21 | asdf | test.cpp:134:25:134:27 | arr |
+| test.cpp:143:18:143:21 | asdf | test.cpp:143:18:143:21 | asdf |
nodes
| test.cpp:35:5:35:22 | access to array | semmle.label | access to array |
| test.cpp:35:10:35:12 | buf | semmle.label | buf |
@@ -36,6 +40,11 @@ nodes
| test.cpp:79:32:79:34 | buf | semmle.label | buf |
| test.cpp:128:9:128:11 | arr | semmle.label | arr |
| test.cpp:128:9:128:14 | access to array | semmle.label | access to array |
+| test.cpp:134:25:134:27 | arr | semmle.label | arr |
+| test.cpp:136:9:136:16 | ... += ... | semmle.label | ... += ... |
+| test.cpp:138:13:138:15 | arr | semmle.label | arr |
+| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
+| test.cpp:143:18:143:21 | asdf | semmle.label | asdf |
subpaths
#select
| test.cpp:35:5:35:22 | PointerAdd: access to array | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:35:5:35:26 | Store: ... = ... | write |
@@ -48,3 +57,4 @@ subpaths
| test.cpp:72:5:72:15 | PointerAdd: access to array | test.cpp:79:32:79:34 | buf | test.cpp:72:5:72:15 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:72:5:72:19 | Store: ... = ... | write |
| test.cpp:77:27:77:44 | PointerAdd: access to array | test.cpp:77:32:77:34 | buf | test.cpp:66:32:66:32 | p | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:15:9:15:11 | buf | buf | test.cpp:67:5:67:10 | Store: ... = ... | write |
| test.cpp:128:9:128:14 | PointerAdd: access to array | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:125:11:125:13 | arr | arr | test.cpp:128:9:128:18 | Store: ... = ... | write |
+| test.cpp:136:9:136:16 | PointerAdd: ... += ... | test.cpp:143:18:143:21 | asdf | test.cpp:138:13:138:15 | arr | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:142:10:142:13 | asdf | asdf | test.cpp:138:12:138:15 | Load: * ... | read |
diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp
index 64b32b9814e..f799518f6ec 100644
--- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp
+++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/constant-size/test.cpp
@@ -85,7 +85,7 @@ void testCharIndex(BigArray *arr) {
char *charBuf = (char*) arr->buf;
charBuf[MAX_SIZE_BYTES - 1] = 0; // GOOD
- charBuf[MAX_SIZE_BYTES] = 0; // BAD [FALSE NEGATIVE]
+ charBuf[MAX_SIZE_BYTES] = 0; // BAD
}
void testEqRefinement() {
@@ -128,3 +128,17 @@ void testStackAllocated() {
arr[i] = 0; // BAD
}
}
+
+int strncmp(const char*, const char*, int);
+
+char testStrncmp2(char *arr) {
+ if(strncmp(arr, "", 6) == 0) {
+ arr += 6;
+ }
+ return *arr; // GOOD [FALSE POSITIVE]
+}
+
+void testStrncmp1() {
+ char asdf[5];
+ testStrncmp2(asdf);
+}
From 06c83ee14da3b7c764fa044e8b6dc7157247f9e3 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Fri, 19 May 2023 11:18:10 -0400
Subject: [PATCH 286/813] Java: add error message for deprecated sink kinds to
'getInvalidModelKind'
---
.../code/java/dataflow/ExternalFlow.qll | 66 ++++++++++++++++++-
1 file changed, 64 insertions(+), 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index 5776d64f402..f933a615c83 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -265,13 +265,72 @@ module ModelValidation {
)
}
+ private class DeprecatedSinkKind extends string {
+ DeprecatedSinkKind() {
+ 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" and result = "\"sql-injection\""
+ or
+ this = "url-redirect" and result = "\"url-redirection\""
+ or
+ this = "xpath" and result = "\"xpath-injection\""
+ or
+ this = "ssti" and result = "\"template-injection\""
+ or
+ this = "logging" and result = "\"log-injection\""
+ or
+ this = "groovy" and result = "\"groovy-injection\""
+ or
+ this = "jexl" and result = "\"jexl-injection\""
+ or
+ this = "mvel" and result = "\"mvel-injection\""
+ or
+ this = "xslt" and result = "\"xslt-injection\""
+ or
+ this = "ldap" and result = "\"ldap-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" and result = "\"path-injection\""
+ or
+ this = "read-file" and result = "\"path-injection\""
+ or
+ this = "open-url" and result = "\"request-forgery\""
+ or
+ this = "jdbc-url" and result = "\"request-forgery\""
+ }
+
+ string deprecationMessage() {
+ result =
+ "The kind \"" + this + "\" is deprecated. Use " + this.replacementKind() + " instead."
+ }
+ }
+
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, _) |
+ exists(string kind, string msg | sinkModel(_, _, _, _, _, _, _, kind, _) |
not kind =
[
"request-forgery", "jndi-injection", "ldap-injection", "sql-injection", "log-injection",
@@ -283,7 +342,10 @@ module ModelValidation {
] and
not kind.matches("regex-use%") and
not kind.matches("qltest%") and
- result = "Invalid kind \"" + kind + "\" in sink model."
+ msg = "Invalid kind \"" + kind + "\" in sink model." and
+ if kind instanceof DeprecatedSinkKind
+ then result = msg + " " + kind.(DeprecatedSinkKind).deprecationMessage()
+ else result = msg
)
or
exists(string kind | sourceModel(_, _, _, _, _, _, _, kind, _) |
From b3d218a50322041d2c4eb275a66afe188e53c702 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 22 May 2023 10:15:34 -0400
Subject: [PATCH 287/813] Java: condense 'replacementKind' code
---
.../code/java/dataflow/ExternalFlow.qll | 43 ++++++-------------
1 file changed, 14 insertions(+), 29 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index f933a615c83..1b0ce54af38 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -277,50 +277,35 @@ module ModelValidation {
}
private string replacementKind() {
- this = "sql" and result = "\"sql-injection\""
+ this = ["sql", "xpath", "groovy", "jexl", "mvel", "xslt", "ldap"] and
+ result = this + "-injection"
or
- this = "url-redirect" and result = "\"url-redirection\""
+ this = "url-redirect" and result = "url-redirection"
or
- this = "xpath" and result = "\"xpath-injection\""
+ this = "ssti" and result = "template-injection"
or
- this = "ssti" and result = "\"template-injection\""
+ this = "logging" and result = "log-injection"
or
- this = "logging" and result = "\"log-injection\""
+ this = "pending-intent-sent" and result = "pending-intents"
or
- this = "groovy" and result = "\"groovy-injection\""
+ this = "intent-start" and result = "intent-redirection"
or
- this = "jexl" and result = "\"jexl-injection\""
+ this = "set-hostname-verifier" and result = "hostname-verification"
or
- this = "mvel" and result = "\"mvel-injection\""
+ this = "header-splitting" and result = "response-splitting"
or
- this = "xslt" and result = "\"xslt-injection\""
+ this = "xss" and result = "html-injection\" or \"js-injection"
or
- this = "ldap" and result = "\"ldap-injection\""
+ this = "write-file" and result = "file-content-store"
or
- this = "pending-intent-sent" and result = "\"pending-intents\""
+ this = ["create-file", "read-file"] and result = "path-injection"
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" and result = "\"path-injection\""
- or
- this = "read-file" and result = "\"path-injection\""
- or
- this = "open-url" and result = "\"request-forgery\""
- or
- this = "jdbc-url" and result = "\"request-forgery\""
+ this = ["open-url", "jdbc-url"] and result = "request-forgery"
}
string deprecationMessage() {
result =
- "The kind \"" + this + "\" is deprecated. Use " + this.replacementKind() + " instead."
+ "The kind \"" + this + "\" is deprecated. Use \"" + this.replacementKind() + "\" instead."
}
}
From 0355b78f13845ab90ce109dae58f5f5ce4a9bd83 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 22 May 2023 10:34:56 -0400
Subject: [PATCH 288/813] Java: add deprecation deletion comment
---
java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 3 +++
1 file changed, 3 insertions(+)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index 1b0ce54af38..0304e64398f 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -247,6 +247,8 @@ module ModelValidation {
)
}
+ /**
+ */
private string getInvalidModelOutput() {
exists(string pred, AccessPath output, AccessPathToken part |
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
@@ -328,6 +330,7 @@ module ModelValidation {
not kind.matches("regex-use%") and
not kind.matches("qltest%") and
msg = "Invalid kind \"" + kind + "\" in sink model." and
+ // The deprecation part of this message can be deleted after June 1st, 2024.
if kind instanceof DeprecatedSinkKind
then result = msg + " " + kind.(DeprecatedSinkKind).deprecationMessage()
else result = msg
From d10857fbdb8991bd7630fb7eae85e67089a1f47a Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Mon, 22 May 2023 10:39:59 -0400
Subject: [PATCH 289/813] Java: fix typo blank qldoc
---
java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll | 2 --
1 file changed, 2 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index 0304e64398f..cebf330c8e4 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -247,8 +247,6 @@ module ModelValidation {
)
}
- /**
- */
private string getInvalidModelOutput() {
exists(string pred, AccessPath output, AccessPathToken part |
sourceModel(_, _, _, _, _, _, output, _, _) and pred = "source"
From b8cedfa817b6b4bc8d56cad8d3cf6faf4da69b01 Mon Sep 17 00:00:00 2001
From: Jami Cogswell
Date: Thu, 1 Jun 2023 13:30:27 -0400
Subject: [PATCH 290/813] Java: switch 'deprecated' to 'outdated'
---
.../lib/semmle/code/java/dataflow/ExternalFlow.qll | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
index cebf330c8e4..ca662ee5610 100644
--- a/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
+++ b/java/ql/lib/semmle/code/java/dataflow/ExternalFlow.qll
@@ -265,8 +265,8 @@ module ModelValidation {
)
}
- private class DeprecatedSinkKind extends string {
- DeprecatedSinkKind() {
+ private class OutdatedSinkKind extends string {
+ OutdatedSinkKind() {
this =
[
"sql", "url-redirect", "xpath", "ssti", "logging", "groovy", "jexl", "mvel", "xslt",
@@ -303,9 +303,9 @@ module ModelValidation {
this = ["open-url", "jdbc-url"] and result = "request-forgery"
}
- string deprecationMessage() {
+ string outdatedMessage() {
result =
- "The kind \"" + this + "\" is deprecated. Use \"" + this.replacementKind() + "\" instead."
+ "The kind \"" + this + "\" is outdated. Use \"" + this.replacementKind() + "\" instead."
}
}
@@ -328,9 +328,9 @@ module ModelValidation {
not kind.matches("regex-use%") and
not kind.matches("qltest%") and
msg = "Invalid kind \"" + kind + "\" in sink model." and
- // The deprecation part of this message can be deleted after June 1st, 2024.
- if kind instanceof DeprecatedSinkKind
- then result = msg + " " + kind.(DeprecatedSinkKind).deprecationMessage()
+ // 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
From f4b68fb8c3c7c821770d99c7966e6421a2754986 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Thu, 1 Jun 2023 21:51:43 +0200
Subject: [PATCH 291/813] bump TypeScript to stable version
---
javascript/extractor/lib/typescript/package.json | 2 +-
javascript/extractor/lib/typescript/yarn.lock | 8 ++++----
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/javascript/extractor/lib/typescript/package.json b/javascript/extractor/lib/typescript/package.json
index cb053bb0e6f..3190b683d34 100644
--- a/javascript/extractor/lib/typescript/package.json
+++ b/javascript/extractor/lib/typescript/package.json
@@ -2,7 +2,7 @@
"name": "typescript-parser-wrapper",
"private": true,
"dependencies": {
- "typescript": "5.1.1-rc"
+ "typescript": "5.1.3"
},
"scripts": {
"build": "tsc --project tsconfig.json",
diff --git a/javascript/extractor/lib/typescript/yarn.lock b/javascript/extractor/lib/typescript/yarn.lock
index 3b0a9476df6..355c257cf69 100644
--- a/javascript/extractor/lib/typescript/yarn.lock
+++ b/javascript/extractor/lib/typescript/yarn.lock
@@ -7,7 +7,7 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014"
integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw==
-typescript@5.1.1-rc:
- version "5.1.1-rc"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.1-rc.tgz#7be6e85bb4ad36e07e0125e501eb08ed3a6e3769"
- integrity sha512-+yHTPe5QCxw5cgN+B81z+k65xTHcwNCRwJN7OGVUe3srPULTZHF7J9QCgrptL7F8mrO7gmsert7XrMksAjutRw==
+typescript@5.1.3:
+ version "5.1.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.1.3.tgz#8d84219244a6b40b6fb2b33cc1c062f715b9e826"
+ integrity sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==
From 97afa5733b86ba10066cec4a5507b08c39d9a772 Mon Sep 17 00:00:00 2001
From: erik-krogh
Date: Thu, 1 Jun 2023 21:52:14 +0200
Subject: [PATCH 292/813] add support for namespaced JSX attributes
---
.../ts/extractor/TypeScriptASTConverter.java | 7 +-
.../test/library-tests/JSX/printAst.expected | 104 ++++++++++++++++++
.../ql/test/library-tests/JSX/tests.expected | 9 ++
.../ql/test/library-tests/JSX/tstest.tsx | 12 ++
4 files changed, 131 insertions(+), 1 deletion(-)
diff --git a/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java b/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java
index e34d552b777..7b68106bb3f 100644
--- a/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java
+++ b/javascript/extractor/src/com/semmle/ts/extractor/TypeScriptASTConverter.java
@@ -1552,8 +1552,13 @@ public class TypeScriptASTConverter {
}
private Node convertJsxAttribute(JsonObject node, SourceLocation loc) throws ParseError {
+ JsonObject nameNode = node.get("name").getAsJsonObject();
+ if (nameNode.get("name") != null) {
+ // it's a namespaced attribute
+ nameNode = nameNode.get("name").getAsJsonObject();
+ }
return new JSXAttribute(
- loc, convertJSXName(convertChild(node, "name")), convertChild(node, "initializer"));
+ loc, convertJSXName(((Expression)convertNode(nameNode, null))), convertChild(node, "initializer")); // 2
}
private Node convertJsxClosingElement(JsonObject node, SourceLocation loc) throws ParseError {
diff --git a/javascript/ql/test/library-tests/JSX/printAst.expected b/javascript/ql/test/library-tests/JSX/printAst.expected
index 11bf254a890..a34e194cca8 100644
--- a/javascript/ql/test/library-tests/JSX/printAst.expected
+++ b/javascript/ql/test/library-tests/JSX/printAst.expected
@@ -3,10 +3,14 @@ nodes
| file://:0:0:0:0 | (Attributes) | semmle.label | (Attributes) |
| file://:0:0:0:0 | (Attributes) | semmle.label | (Attributes) |
| file://:0:0:0:0 | (Attributes) | semmle.label | (Attributes) |
+| file://:0:0:0:0 | (Attributes) | semmle.label | (Attributes) |
+| file://:0:0:0:0 | (Attributes) | semmle.label | (Attributes) |
| file://:0:0:0:0 | (Body) | semmle.label | (Body) |
| file://:0:0:0:0 | (Body) | semmle.label | (Body) |
| file://:0:0:0:0 | (Body) | semmle.label | (Body) |
| file://:0:0:0:0 | (Body) | semmle.label | (Body) |
+| file://:0:0:0:0 | (Body) | semmle.label | (Body) |
+| file://:0:0:0:0 | (Parameters) | semmle.label | (Parameters) |
| tst.js:1:1:1:32 | [DeclStmt] var href = ... | semmle.label | [DeclStmt] var href = ... |
| tst.js:1:1:1:32 | [DeclStmt] var href = ... | semmle.order | 1 |
| tst.js:1:5:1:8 | [VarDecl] href | semmle.label | [VarDecl] href |
@@ -119,6 +123,42 @@ nodes
| tstest.tsx:7:33:7:38 | [JsxElement] | semmle.label | [JsxElement] |
| tstest.tsx:7:34:7:36 | [VarRef] Foo | semmle.label | [VarRef] Foo |
| tstest.tsx:7:40:7:49 | [Literal] more text | semmle.label | [Literal] more text |
+| tstest.tsx:10:1:10:30 | [DeclStmt] const x = ... | semmle.label | [DeclStmt] const x = ... |
+| tstest.tsx:10:1:10:30 | [DeclStmt] const x = ... | semmle.order | 15 |
+| tstest.tsx:10:7:10:7 | [VarDecl] x | semmle.label | [VarDecl] x |
+| tstest.tsx:10:7:10:29 | [VariableDeclarator] x = | semmle.label | [VariableDeclarator] x = |
+| tstest.tsx:10:11:10:29 | [JsxElement] | semmle.label | [JsxElement] |
+| tstest.tsx:10:12:10:14 | [VarRef] Bar | semmle.label | [VarRef] Bar |
+| tstest.tsx:10:16:10:26 | [JsxAttribute] a:b="hello" | semmle.label | [JsxAttribute] a:b="hello" |
+| tstest.tsx:10:18:10:18 | [Label] b | semmle.label | [Label] b |
+| tstest.tsx:10:20:10:26 | [Literal] "hello" | semmle.label | [Literal] "hello" |
+| tstest.tsx:11:1:11:32 | [DeclStmt] const y = ... | semmle.label | [DeclStmt] const y = ... |
+| tstest.tsx:11:1:11:32 | [DeclStmt] const y = ... | semmle.order | 16 |
+| tstest.tsx:11:7:11:7 | [VarDecl] y | semmle.label | [VarDecl] y |
+| tstest.tsx:11:7:11:31 | [VariableDeclarator] y = | semmle.label | [VariableDeclarator] y = |
+| tstest.tsx:11:11:11:31 | [JsxElement] | semmle.label | [JsxElement] |
+| tstest.tsx:11:12:11:14 | [VarRef] Bar | semmle.label | [VarRef] Bar |
+| tstest.tsx:11:16:11:28 | [JsxAttribute] a : b="hello" | semmle.label | [JsxAttribute] a : b="hello" |
+| tstest.tsx:11:20:11:20 | [Label] b | semmle.label | [Label] b |
+| tstest.tsx:11:22:11:28 | [Literal] "hello" | semmle.label | [Literal] "hello" |
+| tstest.tsx:13:1:15:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ring; } | semmle.label | [InterfaceDeclaration,TypeDefinition] interfa ... ring; } |
+| tstest.tsx:13:1:15:1 | [InterfaceDeclaration,TypeDefinition] interfa ... ring; } | semmle.order | 17 |
+| tstest.tsx:13:11:13:18 | [Identifier] BarProps | semmle.label | [Identifier] BarProps |
+| tstest.tsx:14:5:14:9 | [Literal] "a:b" | semmle.label | [Literal] "a:b" |
+| tstest.tsx:14:5:14:18 | [FieldDeclaration] "a:b": string; | semmle.label | [FieldDeclaration] "a:b": string; |
+| tstest.tsx:14:12:14:17 | [KeywordTypeExpr] string | semmle.label | [KeywordTypeExpr] string |
+| tstest.tsx:17:1:19:1 | [FunctionDeclStmt] functio ... div>; } | semmle.label | [FunctionDeclStmt] functio ... div>; } |
+| tstest.tsx:17:1:19:1 | [FunctionDeclStmt] functio ... div>; } | semmle.order | 18 |
+| tstest.tsx:17:10:17:12 | [VarDecl] Bar | semmle.label | [VarDecl] Bar |
+| tstest.tsx:17:14:17:18 | [SimpleParameter] props | semmle.label | [SimpleParameter] props |
+| tstest.tsx:17:21:17:28 | [LocalTypeAccess] BarProps | semmle.label | [LocalTypeAccess] BarProps |
+| tstest.tsx:17:31:19:1 | [BlockStmt] { r ... div>; } | semmle.label | [BlockStmt] { r ... div>; } |
+| tstest.tsx:18:5:18:37 | [ReturnStmt] return ... ; | semmle.label | [ReturnStmt] return ... ; |
+| tstest.tsx:18:12:18:36 | [JsxElement]