From a276cc3094862274e7a4411cf53ec2b8cb00065f Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Wed, 19 Apr 2023 17:09:24 +0200 Subject: [PATCH 01/93] Convert all command injection sinks to MaD format --- .../2023-04-19-deprecated-execcallable.md | 4 +++ .../experimental/com.jcraft.jsch.model.yml | 6 ++++ java/ql/lib/ext/java.lang.model.yml | 27 ++++++++------- .../lib/ext/org.apache.commons.exec.model.yml | 9 +++++ java/ql/lib/semmle/code/java/JDK.qll | 34 ------------------- .../code/java/frameworks/apache/Exec.qll | 29 ---------------- .../code/java/security/CommandLineQuery.qll | 10 +++--- .../code/java/security/ExternalProcess.qll | 31 +++++++++-------- .../Security/CWE/CWE-078/ExecTaintedLocal.ql | 11 +++--- .../src/Security/CWE/CWE-078/ExecUnescaped.ql | 1 + .../Security/CWE/CWE-078/ExecTainted.ql | 6 +++- .../Security/CWE/CWE-078/JSchOSInjection.qll | 20 ----------- .../CWE-078/ExecTaintedLocal.expected | 20 +++++++---- 13 files changed, 81 insertions(+), 127 deletions(-) create mode 100644 java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md create mode 100644 java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml create mode 100644 java/ql/lib/ext/org.apache.commons.exec.model.yml delete mode 100644 java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll delete mode 100644 java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll diff --git a/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md b/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md new file mode 100644 index 00000000000..fc21d1825bf --- /dev/null +++ b/java/ql/lib/change-notes/2023-04-19-deprecated-execcallable.md @@ -0,0 +1,4 @@ +--- +category: deprecated +--- +* The `ExecCallable` class in `ExternalProcess.qll` has been deprecated. diff --git a/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml b/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml new file mode 100644 index 00000000000..1a8783d91a5 --- /dev/null +++ b/java/ql/lib/ext/experimental/com.jcraft.jsch.model.yml @@ -0,0 +1,6 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: experimentalSinkModel + data: + - ["com.jcraft.jsch", "ChannelExec", True, "setCommand", "", "", "Argument[0]", "command-injection", "manual", "jsch-os-injection"] diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml index bbb269b3d55..59eb548af27 100644 --- a/java/ql/lib/ext/java.lang.model.yml +++ b/java/ql/lib/ext/java.lang.model.yml @@ -8,19 +8,20 @@ extensions: - ["java.lang", "ClassLoader", True, "getSystemResource", "(String)", "", "Argument[0]", "read-file", "ai-manual"] - ["java.lang", "ClassLoader", True, "getSystemResourceAsStream", "(String)", "", "Argument[0]", "read-file", "ai-manual"] - ["java.lang", "Module", True, "getResourceAsStream", "(String)", "", "Argument[0]", "read-file", "ai-manual"] - # These are modeled in plain CodeQL. TODO: migrate them. - # - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] - # - ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String[],String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "Runtime", True, "exec", "(String,String[],File)", "", "Argument[2]", "command-injection", "ai-manual"] - ["java.lang", "String", False, "matches", "(String)", "", "Argument[0]", "regex-use[f-1]", "manual"] - ["java.lang", "String", False, "replaceAll", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"] - ["java.lang", "String", False, "replaceFirst", "(String,String)", "", "Argument[0]", "regex-use[-1]", "manual"] diff --git a/java/ql/lib/ext/org.apache.commons.exec.model.yml b/java/ql/lib/ext/org.apache.commons.exec.model.yml new file mode 100644 index 00000000000..7b65ee46a49 --- /dev/null +++ b/java/ql/lib/ext/org.apache.commons.exec.model.yml @@ -0,0 +1,9 @@ +extensions: + - addsTo: + pack: codeql/java-all + extensible: sinkModel + data: + - ["org.apache.commons.exec", "CommandLine", True, "parse", "(String)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "parse", "(String,Map)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String,boolean)", "", "Argument[0]", "command-injection", "manual"] diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 78f7defc32f..62115884c62 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -3,7 +3,6 @@ */ import Member -import semmle.code.java.security.ExternalProcess private import semmle.code.java.dataflow.FlowSteps // --- Standard types --- @@ -198,39 +197,6 @@ class TypeFile extends Class { } // --- Standard methods --- -/** - * Any constructor of class `java.lang.ProcessBuilder`. - */ -class ProcessBuilderConstructor extends Constructor, ExecCallable { - ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder } - - override int getAnExecutedArgument() { result = 0 } -} - -/** - * Any of the methods named `command` on class `java.lang.ProcessBuilder`. - */ -class MethodProcessBuilderCommand extends Method, ExecCallable { - MethodProcessBuilderCommand() { - this.hasName("command") and - this.getDeclaringType() instanceof TypeProcessBuilder - } - - override int getAnExecutedArgument() { result = 0 } -} - -/** - * Any method named `exec` on class `java.lang.Runtime`. - */ -class MethodRuntimeExec extends Method, ExecCallable { - MethodRuntimeExec() { - this.hasName("exec") and - this.getDeclaringType() instanceof TypeRuntime - } - - override int getAnExecutedArgument() { result = 0 } -} - /** * Any method named `getenv` on class `java.lang.System`. */ diff --git a/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll b/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll deleted file mode 100644 index d6876bfae70..00000000000 --- a/java/ql/lib/semmle/code/java/frameworks/apache/Exec.qll +++ /dev/null @@ -1,29 +0,0 @@ -/** Definitions related to the Apache Commons Exec library. */ - -import semmle.code.java.Type -import semmle.code.java.security.ExternalProcess - -/** The class `org.apache.commons.exec.CommandLine`. */ -private class TypeCommandLine extends Class { - TypeCommandLine() { this.hasQualifiedName("org.apache.commons.exec", "CommandLine") } -} - -/** The `parse()` method of the class `org.apache.commons.exec.CommandLine`. */ -private class MethodCommandLineParse extends Method, ExecCallable { - MethodCommandLineParse() { - this.getDeclaringType() instanceof TypeCommandLine and - this.hasName("parse") - } - - override int getAnExecutedArgument() { result = 0 } -} - -/** The `addArguments()` method of the class `org.apache.commons.exec.CommandLine`. */ -private class MethodCommandLineAddArguments extends Method, ExecCallable { - MethodCommandLineAddArguments() { - this.getDeclaringType() instanceof TypeCommandLine and - this.hasName("addArguments") - } - - override int getAnExecutedArgument() { result = 0 } -} diff --git a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll index b6e3f5b188a..c0d09a9eeab 100644 --- a/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll +++ b/java/ql/lib/semmle/code/java/security/CommandLineQuery.qll @@ -10,8 +10,8 @@ import java private import semmle.code.java.dataflow.FlowSources private import semmle.code.java.dataflow.ExternalFlow -private import semmle.code.java.security.ExternalProcess private import semmle.code.java.security.CommandArguments +private import semmle.code.java.security.ExternalProcess /** A sink for command injection vulnerabilities. */ abstract class CommandInjectionSink extends DataFlow::Node { } @@ -33,9 +33,7 @@ class CommandInjectionAdditionalTaintStep extends Unit { } private class DefaultCommandInjectionSink extends CommandInjectionSink { - DefaultCommandInjectionSink() { - this.asExpr() instanceof ArgumentToExec or sinkNode(this, "command-injection") - } + DefaultCommandInjectionSink() { sinkNode(this, "command-injection") } } private class DefaultCommandInjectionSanitizer extends CommandInjectionSanitizer { @@ -100,7 +98,7 @@ predicate execIsTainted( RemoteUserInputToArgumentToExecFlow::PathNode sink, Expr execArg ) { RemoteUserInputToArgumentToExecFlow::flowPath(source, sink) and - sink.getNode().asExpr() = execArg + argumentToExec(execArg, sink.getNode()) } /** @@ -112,7 +110,7 @@ predicate execIsTainted( */ deprecated predicate execTainted(DataFlow::PathNode source, DataFlow::PathNode sink, Expr execArg) { exists(RemoteUserInputToArgumentToExecFlowConfig conf | - conf.hasFlowPath(source, sink) and sink.getNode().asExpr() = execArg + conf.hasFlowPath(source, sink) and argumentToExec(execArg, sink.getNode()) ) } diff --git a/java/ql/lib/semmle/code/java/security/ExternalProcess.qll b/java/ql/lib/semmle/code/java/security/ExternalProcess.qll index 9a061c7a419..385d2f6c548 100644 --- a/java/ql/lib/semmle/code/java/security/ExternalProcess.qll +++ b/java/ql/lib/semmle/code/java/security/ExternalProcess.qll @@ -1,16 +1,13 @@ /** Definitions related to external processes. */ import semmle.code.java.Member - -private module Instances { - private import semmle.code.java.JDK - private import semmle.code.java.frameworks.apache.Exec -} +private import semmle.code.java.dataflow.DataFlow +private import semmle.code.java.security.CommandLineQuery /** - * A callable that executes a command. + * DEPRECATED: A callable that executes a command. */ -abstract class ExecCallable extends Callable { +abstract deprecated class ExecCallable extends Callable { /** * Gets the index of an argument that will be part of the command that is executed. */ @@ -23,13 +20,19 @@ abstract class ExecCallable extends Callable { * to be executed. */ class ArgumentToExec extends Expr { - ArgumentToExec() { - exists(Call execCall, ExecCallable execCallable, int i | - execCall.getArgument(pragma[only_bind_into](i)) = this and - execCallable = execCall.getCallee() and - i = execCallable.getAnExecutedArgument() - ) - } + ArgumentToExec() { argumentToExec(this, _) } +} + +/** + * Holds if `e` is an expression used as an argument to a call that executes an external command. + * For calls to varargs method calls, this only includes the first argument, which will be the command + * to be executed. + */ +predicate argumentToExec(Expr e, CommandInjectionSink s) { + s.asExpr() = e + or + e.(Argument).isNthVararg(0) and + s.(DataFlow::ImplicitVarargsArray).getCall() = e.(Argument).getCall() } /** diff --git a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql index 08c230cb43a..38b79c468cd 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecTaintedLocal.ql @@ -14,11 +14,14 @@ import java import semmle.code.java.security.CommandLineQuery +import semmle.code.java.security.ExternalProcess import LocalUserInputToArgumentToExecFlow::PathGraph from LocalUserInputToArgumentToExecFlow::PathNode source, - LocalUserInputToArgumentToExecFlow::PathNode sink -where LocalUserInputToArgumentToExecFlow::flowPath(source, sink) -select sink.getNode().asExpr(), source, sink, "This command line depends on a $@.", - source.getNode(), "user-provided value" + LocalUserInputToArgumentToExecFlow::PathNode sink, Expr e +where + LocalUserInputToArgumentToExecFlow::flowPath(source, sink) and + argumentToExec(e, sink.getNode()) +select e, source, sink, "This command line depends on a $@.", source.getNode(), + "user-provided value" diff --git a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql index 68e3cc2faa7..d50f583bbfe 100644 --- a/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql +++ b/java/ql/src/Security/CWE/CWE-078/ExecUnescaped.ql @@ -14,6 +14,7 @@ import java import semmle.code.java.security.CommandLineQuery +import semmle.code.java.security.ExternalProcess /** * Strings that are known to be sane by some simple local analysis. Such strings diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql index 4305b9fbabc..5d543d65011 100644 --- a/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql +++ b/java/ql/src/experimental/Security/CWE/CWE-078/ExecTainted.ql @@ -15,7 +15,11 @@ import java import semmle.code.java.security.CommandLineQuery import RemoteUserInputToArgumentToExecFlow::PathGraph -import JSchOSInjection +private import semmle.code.java.dataflow.ExternalFlow + +private class ActivateModels extends ActiveExperimentalModels { + ActivateModels() { this = "jsch-os-injection" } +} // This is a clone of query `java/command-line-injection` that also includes experimental sinks. from diff --git a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll b/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll deleted file mode 100644 index ec1f4d0adfa..00000000000 --- a/java/ql/src/experimental/Security/CWE/CWE-078/JSchOSInjection.qll +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Provides classes for JSch OS command injection detection - */ - -import java - -/** The class `com.jcraft.jsch.ChannelExec`. */ -private class JSchChannelExec extends RefType { - JSchChannelExec() { this.hasQualifiedName("com.jcraft.jsch", "ChannelExec") } -} - -/** A method to set an OS Command for the execution. */ -private class ChannelExecSetCommandMethod extends Method, ExecCallable { - ChannelExecSetCommandMethod() { - this.hasName("setCommand") and - this.getDeclaringType() instanceof JSchChannelExec - } - - override int getAnExecutedArgument() { result = 0 } -} diff --git a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected index 2ae893b5d1d..4616bd7c808 100644 --- a/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected +++ b/java/ql/test/query-tests/security/CWE-078/ExecTaintedLocal.expected @@ -1,22 +1,28 @@ edges -| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... | +| Test.java:6:35:6:44 | arg : String | Test.java:7:44:7:69 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:10:61:10:73 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:16:13:16:25 | ... + ... : String | | Test.java:6:35:6:44 | arg : String | Test.java:22:15:22:27 | ... + ... : String | +| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | Test.java:7:25:7:70 | new ..[] { .. } | +| Test.java:7:44:7:69 | ... + ... : String | Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | | Test.java:10:29:10:74 | {...} : String[] [[]] : String | Test.java:10:29:10:74 | new String[] | | Test.java:10:61:10:73 | ... + ... : String | Test.java:10:29:10:74 | {...} : String[] [[]] : String | | Test.java:16:5:16:7 | cmd [post update] : List [] : String | Test.java:18:29:18:31 | cmd | | Test.java:16:13:16:25 | ... + ... : String | Test.java:16:5:16:7 | cmd [post update] : List [] : String | | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | Test.java:24:29:24:32 | cmd1 | | Test.java:22:15:22:27 | ... + ... : String | Test.java:22:5:22:8 | cmd1 [post update] : String[] [[]] : String | -| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... | +| Test.java:28:38:28:47 | arg : String | Test.java:29:44:29:64 | ... + ... : String | +| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | Test.java:29:25:29:65 | new ..[] { .. } | +| Test.java:29:44:29:64 | ... + ... : String | Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | | Test.java:57:27:57:39 | args : String[] | Test.java:60:20:60:22 | arg : String | | Test.java:57:27:57:39 | args : String[] | Test.java:61:23:61:25 | arg : String | | Test.java:60:20:60:22 | arg : String | Test.java:6:35:6:44 | arg : String | | Test.java:61:23:61:25 | arg : String | Test.java:28:38:28:47 | arg : String | nodes | Test.java:6:35:6:44 | arg : String | semmle.label | arg : String | -| Test.java:7:44:7:69 | ... + ... | semmle.label | ... + ... | +| Test.java:7:25:7:70 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:7:25:7:70 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String | +| Test.java:7:44:7:69 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:10:29:10:74 | new String[] | semmle.label | new String[] | | Test.java:10:29:10:74 | {...} : String[] [[]] : String | semmle.label | {...} : String[] [[]] : String | | Test.java:10:61:10:73 | ... + ... : String | semmle.label | ... + ... : String | @@ -27,14 +33,16 @@ nodes | Test.java:22:15:22:27 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:24:29:24:32 | cmd1 | semmle.label | cmd1 | | Test.java:28:38:28:47 | arg : String | semmle.label | arg : String | -| Test.java:29:44:29:64 | ... + ... | semmle.label | ... + ... | +| Test.java:29:25:29:65 | new ..[] { .. } | semmle.label | new ..[] { .. } | +| Test.java:29:25:29:65 | new ..[] { .. } : String[] [[]] : String | semmle.label | new ..[] { .. } : String[] [[]] : String | +| Test.java:29:44:29:64 | ... + ... : String | semmle.label | ... + ... : String | | Test.java:57:27:57:39 | args : String[] | semmle.label | args : String[] | | Test.java:60:20:60:22 | arg : String | semmle.label | arg : String | | Test.java:61:23:61:25 | arg : String | semmle.label | arg : String | subpaths #select -| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:44:7:69 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | +| Test.java:7:44:7:69 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:7:25:7:70 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:10:29:10:74 | new String[] | Test.java:57:27:57:39 | args : String[] | Test.java:10:29:10:74 | new String[] | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:18:29:18:31 | cmd | Test.java:57:27:57:39 | args : String[] | Test.java:18:29:18:31 | cmd | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | | Test.java:24:29:24:32 | cmd1 | Test.java:57:27:57:39 | args : String[] | Test.java:24:29:24:32 | cmd1 | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | -| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:44:29:64 | ... + ... | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | +| Test.java:29:44:29:64 | ... + ... | Test.java:57:27:57:39 | args : String[] | Test.java:29:25:29:65 | new ..[] { .. } | This command line depends on a $@. | Test.java:57:27:57:39 | args | user-provided value | From d18fb646d1550b0e12185d7ce95c6483495d5fc1 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Wed, 10 May 2023 15:35:18 -0400 Subject: [PATCH 02/93] C++: handle cast arrays properly in off-by-one query --- .../Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql | 5 ++--- .../CWE-193/constant-size/ConstantSizeArrayOffByOne.expected | 4 ++++ .../query-tests/Security/CWE/CWE-193/constant-size/test.cpp | 2 +- 3 files changed, 7 insertions(+), 4 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..d6bac268b5d 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/ConstantSizeArrayOffByOne.ql @@ -81,8 +81,7 @@ predicate isInvalidPointerDerefSink2(DataFlow::Node sink, Instruction i, string predicate pointerArithOverflow0( PointerArithmeticInstruction pai, Variable v, int size, int bound, int delta ) { - pai.getElementSize() = v.getUnspecifiedType().(ArrayType).getBaseType().getSize() and - v.getUnspecifiedType().(ArrayType).getArraySize() = size and + v.getUnspecifiedType().(ArrayType).getByteSize() / pai.getElementSize() = size and semBounded(getSemanticExpr(pai.getRight()), any(SemZeroBound b), bound, true, _) and delta = bound - size and delta >= 0 and @@ -162,7 +161,7 @@ from Variable v, ArrayAddressToDerefFlow::PathNode source, PointerArithmeticInstruction pai, ArrayAddressToDerefFlow::PathNode sink, Instruction deref, string operation, int delta where -ArrayAddressToDerefFlow::flowPath(source, sink) and + ArrayAddressToDerefFlow::flowPath(source, sink) and isInvalidPointerDerefSink2(sink.getNode(), deref, operation) and source.getState() = ArrayAddressToDerefConfig::TArray(v) and sink.getState() = ArrayAddressToDerefConfig::TOverflowArithmetic(pai) and 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..1555cd3f243 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 @@ -11,6 +11,7 @@ edges | test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | | 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:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | | test.cpp:128:9:128:11 | arr | test.cpp:128:9:128:14 | access to array | nodes | test.cpp:35:5:35:22 | access to array | semmle.label | access to array | @@ -34,6 +35,8 @@ nodes | test.cpp:77:32:77:34 | buf | semmle.label | buf | | test.cpp:79:27:79:34 | buf | semmle.label | buf | | test.cpp:79:32:79:34 | buf | semmle.label | buf | +| test.cpp:85:34:85:36 | buf | semmle.label | buf | +| test.cpp:88:5:88:27 | access to array | semmle.label | access to array | | test.cpp:128:9:128:11 | arr | semmle.label | arr | | test.cpp:128:9:128:14 | access to array | semmle.label | access to array | subpaths @@ -47,4 +50,5 @@ subpaths | test.cpp:61:9:61:19 | PointerAdd: access to array | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | This pointer arithmetic may have an off-by-2 error allowing it to overrun $@ at this $@. | test.cpp:19:9:19:11 | buf | buf | test.cpp:61:9:61:23 | Store: ... = ... | write | | 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:88:5:88:27 | PointerAdd: access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | 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:88:5:88:31 | 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 | 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..952b44209d4 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() { From 2fd2c434f2b419e6db77a4d947c056b08f489578 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 13 Jun 2023 09:24:15 +0200 Subject: [PATCH 03/93] Apply suggestions from code review Co-authored-by: Jami <57204504+jcogs33@users.noreply.github.com> --- java/ql/lib/ext/java.lang.model.yml | 3 +-- java/ql/lib/ext/org.apache.commons.exec.model.yml | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/lib/ext/java.lang.model.yml b/java/ql/lib/ext/java.lang.model.yml index d3737a62d9f..169440fe591 100644 --- a/java/ql/lib/ext/java.lang.model.yml +++ b/java/ql/lib/ext/java.lang.model.yml @@ -8,8 +8,7 @@ extensions: - ["java.lang", "ClassLoader", True, "getSystemResource", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.lang", "ClassLoader", True, "getSystemResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] - ["java.lang", "Module", True, "getResourceAsStream", "(String)", "", "Argument[0]", "path-injection", "ai-manual"] - - ["java.lang", "ProcessBuilder", False, "command", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] - - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] + - ["java.lang", "ProcessBuilder", False, "command", "(List)", "", "Argument[0]", "command-injection", "manual"] - ["java.lang", "ProcessBuilder", False, "command", "(String[])", "", "Argument[0]", "command-injection", "ai-manual"] - ["java.lang", "ProcessBuilder", False, "directory", "(File)", "", "Argument[0]", "command-injection", "ai-manual"] - ["java.lang", "ProcessBuilder", False, "ProcessBuilder", "(List)", "", "Argument[0]", "command-injection", "ai-manual"] diff --git a/java/ql/lib/ext/org.apache.commons.exec.model.yml b/java/ql/lib/ext/org.apache.commons.exec.model.yml index 7b65ee46a49..314b0996194 100644 --- a/java/ql/lib/ext/org.apache.commons.exec.model.yml +++ b/java/ql/lib/ext/org.apache.commons.exec.model.yml @@ -7,3 +7,5 @@ extensions: - ["org.apache.commons.exec", "CommandLine", True, "parse", "(String,Map)", "", "Argument[0]", "command-injection", "manual"] - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String)", "", "Argument[0]", "command-injection", "manual"] - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String,boolean)", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[])", "", "Argument[0]", "command-injection", "manual"] + - ["org.apache.commons.exec", "CommandLine", True, "addArguments", "(String[],boolean)", "", "Argument[0]", "command-injection", "manual"] From 29d4b6fadc170cef201b6c9fc3dbdca5d4f00a0b Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Tue, 13 Jun 2023 09:22:37 +0200 Subject: [PATCH 04/93] Re-add public classes that shouldn't be removed yet --- java/ql/lib/semmle/code/java/JDK.qll | 34 ++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/java/ql/lib/semmle/code/java/JDK.qll b/java/ql/lib/semmle/code/java/JDK.qll index 62115884c62..156cbbc0f93 100644 --- a/java/ql/lib/semmle/code/java/JDK.qll +++ b/java/ql/lib/semmle/code/java/JDK.qll @@ -3,6 +3,7 @@ */ import Member +import semmle.code.java.security.ExternalProcess private import semmle.code.java.dataflow.FlowSteps // --- Standard types --- @@ -197,6 +198,39 @@ class TypeFile extends Class { } // --- Standard methods --- +/** + * DEPRECATED: Any constructor of class `java.lang.ProcessBuilder`. + */ +deprecated class ProcessBuilderConstructor extends Constructor, ExecCallable { + ProcessBuilderConstructor() { this.getDeclaringType() instanceof TypeProcessBuilder } + + override int getAnExecutedArgument() { result = 0 } +} + +/** + * DEPRECATED: Any of the methods named `command` on class `java.lang.ProcessBuilder`. + */ +deprecated class MethodProcessBuilderCommand extends Method, ExecCallable { + MethodProcessBuilderCommand() { + this.hasName("command") and + this.getDeclaringType() instanceof TypeProcessBuilder + } + + override int getAnExecutedArgument() { result = 0 } +} + +/** + * DEPRECATED: Any method named `exec` on class `java.lang.Runtime`. + */ +deprecated class MethodRuntimeExec extends Method, ExecCallable { + MethodRuntimeExec() { + this.hasName("exec") and + this.getDeclaringType() instanceof TypeRuntime + } + + override int getAnExecutedArgument() { result = 0 } +} + /** * Any method named `getenv` on class `java.lang.System`. */ From 0110610c6a25258ec5c03451839604dd13d001b7 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:01:42 +0200 Subject: [PATCH 05/93] Ruby: overhaul API graphs --- .../dataflow/new/internal/TypeTracker.qll | 45 +- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 1827 +++++++++++------ .../ruby/dataflow/internal/DataFlowPublic.qll | 145 +- .../ruby/frameworks/data/ModelsAsData.qll | 2 +- .../data/internal/ApiGraphModelsSpecific.qll | 54 +- .../ruby/typetracking/ApiGraphShared.qll | 329 +++ .../codeql/ruby/typetracking/TypeTracker.qll | 45 +- .../dataflow/api-graphs/ApiGraphs.expected | 8 +- ...ed => VerifyApiGraphExpectations.expected} | 0 .../api-graphs/VerifyApiGraphExpectations.ql | 77 + .../dataflow/api-graphs/callbacks.rb | 52 +- .../dataflow/api-graphs/chained-access.rb | 31 + .../dataflow/api-graphs/method-callbacks.rb | 64 + .../dataflow/api-graphs/test1.rb | 72 +- .../library-tests/dataflow/api-graphs/use.ql | 88 - .../util/test/InlineExpectationsTest.qll | 13 + 16 files changed, 1940 insertions(+), 912 deletions(-) create mode 100644 ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll rename ruby/ql/test/library-tests/dataflow/api-graphs/{use.expected => VerifyApiGraphExpectations.expected} (100%) create mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql create mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb create mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb delete mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/use.ql diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll index 25521f5f1a5..74c67b5be78 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll @@ -55,10 +55,9 @@ private module Cached { ) } - pragma[nomagic] - private TypeTracker noContentTypeTracker(boolean hasCall) { - result = MkTypeTracker(hasCall, noContent()) - } + /** Gets a type tracker with no content and the call bit set to the given value. */ + cached + TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -340,6 +339,8 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { + predicate append = Cached::append/2; + /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -400,6 +401,35 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; + + /** Gets the step summary for a level step. */ + StepSummary levelStep() { result = LevelStep() } + + /** Gets the step summary for a call step. */ + StepSummary callStep() { result = CallStep() } + + /** Gets the step summary for a return step. */ + StepSummary returnStep() { result = ReturnStep() } + + /** Gets the step summary for storing into `content`. */ + StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } + + /** Gets the step summary for loading from `content`. */ + StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } + + /** Gets the step summary for loading from `load` and then storing into `store`. */ + StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { + result = LoadStoreStep(load, store) + } + + /** Gets the step summary for a step that only permits contents matched by `filter`. */ + StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } + + /** Gets the step summary for a step that blocks contents matched by `filter`. */ + StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } + + /** Gets the step summary for a jump step. */ + StepSummary jumpStep() { result = JumpStep() } } /** @@ -545,6 +575,13 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } + + /** + * INTERNAL USE ONLY. + * + * Gets a valid end point of type tracking with the call bit set to the given value. + */ + predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 1cf4c445781..aaa80c3c205 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -1,13 +1,13 @@ /** - * Provides an implementation of _API graphs_, which are an abstract representation of the API - * surface used and/or defined by a code base. + * Provides an implementation of _API graphs_, which allow efficient modelling of how a given + * value is used the code base or how values produced by the code base are consumed by a library. * - * The nodes of the API graph represent definitions and uses of API components. The edges are - * directed and labeled; they specify how the components represented by nodes relate to each other. + * See `API::Node` for more details. */ private import codeql.ruby.AST private import codeql.ruby.DataFlow +private import codeql.ruby.typetracking.ApiGraphShared private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific private import codeql.ruby.controlflow.CfgNodes @@ -19,85 +19,140 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatc */ module API { /** - * A node in the API graph, representing a value that has crossed the boundary between this - * codebase and an external library (or in general, any external codebase). + * A node in the API graph, that is, a value that can be tracked interprocedurally. * - * ### Basic usage + * The API graph is a graph for tracking values of certain types in a way that accounts for inheritance + * and interprocedural data flow. * * API graphs are typically used to identify "API calls", that is, calls to an external function * whose implementation is not necessarily part of the current codebase. * + * ### Basic usage + * * The most basic use of API graphs is typically as follows: * 1. Start with `API::getTopLevelMember` for the relevant library. * 2. Follow up with a chain of accessors such as `getMethod` describing how to get to the relevant API function. - * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`. + * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource`, `asSink`, or `asCall`. * - * For example, a simplified way to get arguments to `Foo.bar` would be - * ```ql - * API::getTopLevelMember("Foo").getMethod("bar").getParameter(0).asSink() + * The following examples demonstrate how to identify the expression `x` in various basic cases: + * ```rb + * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() + * Foo.bar(x) + * + * # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("foo").asSink() + * Foo.bar(foo: x) + * + * # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * Foo.new.bar(x) + * + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() + * end * ``` * - * The most commonly used accessors are `getMember`, `getMethod`, `getParameter`, and `getReturn`. + * ### Data flow * - * ### API graph nodes + * The members predicates on this class generally take inheritance and data flow into account. * - * There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value: - * - **Use-nodes** represent values held by the current codebase, which came from an external library. - * (The current codebase is "using" a value that came from the library). - * - **Def-nodes** represent values held by the external library, which came from this codebase. - * (The current codebase "defines" the value seen by the library). - * - * API graph nodes are associated with data-flow nodes in the current codebase. - * (Since external libraries are not part of the database, there is no way to associate with concrete - * data-flow nodes from the external library). - * - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase, - * such as the return value of a call to an external function. - * - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase, - * such as an argument passed in a call to an external function. - * - * - * ### Access paths and edge labels - * - * Nodes in the API graph are associated with a set of access paths, describing a series of operations - * that may be performed to obtain that value. - * - * For example, the access path `API::getTopLevelMember("Foo").getMethod("bar")` represents the action of - * reading the top-level constant `Foo` and then accessing the method `bar` on the resulting object. - * It would be associated with a call such as `Foo.bar()`. - * - * Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node - * determines who is performing the operation, and the type of the `B` node determines who ends up holding - * the result: - * - An edge starting from a use-node describes what the current codebase is doing to a value that - * came from a library. - * - An edge starting from a def-node describes what the external library might do to a value that - * came from the current codebase. - * - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node). - * - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is - * the place where it was "last seen" in the current codebase before flowing out) - * - * Because the implementation of the external library is not visible, it is not known exactly what operations - * it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would - * lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform - * those operations. (When constructing these edges, we assume the library is somewhat well-behaved). - * - * For example, given this snippet: + * The following example demonstrate a case where data flow was used to find the sink `x`: * ```ruby - * Foo.bar(->(x) { doSomething(x) }) + * def doSomething f + * f.bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * end + * doSomething Foo.new * ``` - * A callback is passed to the external function `Foo.bar`. We can't know if `Foo.bar` will actually invoke this callback. - * But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter. - * For that reason, an edge is generated representing the argument-passing operation that might be performed by `Foo.bar`. - * This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x` of the lambda. + * The call `API::getTopLevelMember("Foo").getInstance()` identifies the `Foo.new` call, and `getMethod("bar")` + * then follows data flow from there to find calls to `bar` where that object flows to the receiver. + * This results in the `f.bar` call. + * + * ### Backward data flow + * + * When inspecting the arguments of a call, the data flow direction is backwards. + * The following example illustrates this when we match the `x` parameter of a block: + * ```ruby + * def doSomething &blk + * Foo.bar &blk + * end + * doSomething do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() + * end + * ``` + * When `getParameter(0)` is evaluated, the API graph backtracks the `&blk` argument to the block argument a few + * lines below. As a result, it eventually matches the `x` parameter of that block. + * + * ### Inheritance + * + * When a class or module object is tracked, inheritance is taken into account. + * + * In the following example, a call to `Foo.bar` was found via a subclass of `Foo`, + * because classes inherit singleton methods from their base class: + * ```ruby + * class Subclass < Foo + * def self.doSomething + * bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() + * end + * end + * ``` + * + * Similarly, instance methods can be found in subclasses, or ancestors of subclases in cases of multiple inheritance: + * ```rb + * module Mixin + * def doSomething + * bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() + * end + * end + * class Subclass < Foo + * include Mixin + * end + * ``` + * The value of `self` in `Mixin#doSomething` is seen as a potential instance of `Foo`, and is thus found by `getTopLevelMember("Foo").getInstance()`. + * This eventually results in finding the call `bar`, due to its implicit `self` receiver, and finally its argument `x` is found as the sink. + * + * ### Backward data flow and classes + * + * When inspecting the arguments of call, and the value flowing into that argument is a user-defined class (or an instance thereof), + * uses of `getMethod` will find method definitions in that class (including inherited ones) rather than finding method calls. + * + * This example illustrates how this can be used to model cases where the library calls a specific named method on a user-defined class: + * ```rb + * class MyClass + * def doSomething + * x # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("doSomething").getReturn().asSink() + * end + * end + * Foo.bar MyClass.new + * ``` + * + * When modeling an external library that is known to call a specific method on a parameter (in this case `doSomething`), this makes + * it possible to find the corresponding method definition in user code. + * + * ### Strict left-to-right evaluation + * + * Most member predicates on this class are intended to be chained, and are always evaluated from left to right, which means + * the caller should restrict the initial set of values. + * + * For example, in the following snippet, we always find the uses of `Foo` before finding calls to `bar`: + * ```ql + * API::getTopLevelMember("Foo").getMethod("bar") + * ``` + * In particular, the implementation will never look for calls to `bar` and work backward from there. + * + * Beware of the footgun that is to use API graphs with an unrestricted receiver: + * ```ql + * API::Node barCall(API::Node base) { + * result = base.getMethod("bar") // Do not do this! + * } + * ``` + * The above predicate does not restrict the receiver, and will thus perform an interprocedural data flow + * search starting at every node in the graph, which is very expensive. */ class Node extends Impl::TApiNode { /** - * Gets a data-flow node where this value may flow after entering the current codebase. + * Gets a data-flow node where this value may flow interprocedurally. * * This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow. * See `asSource()` for examples. */ - pragma[inline] + bindingset[this] + pragma[inline_late] DataFlow::Node getAValueReachableFromSource() { result = getAValueReachableFromSourceInline(this) } @@ -119,16 +174,14 @@ module API { * end * ``` */ - pragma[inline] - DataFlow::LocalSourceNode asSource() { - result = pragma[only_bind_out](this).(Node::Internal).asSourceInternal() - } + bindingset[this] + pragma[inline_late] + DataFlow::LocalSourceNode asSource() { result = asSourceInline(this) } /** - * Gets a data-flow node where this value leaves the current codebase and flows into an - * external library (or in general, any external codebase). + * Gets a data-flow node where this value potentially flows into an external library. * - * Concretely, this corresponds to an argument passed to a call to external code. + * This is usually the argument of a call, but can also be the return value of a callback. * * For example: * ```ruby @@ -143,15 +196,425 @@ module API { * }) * ``` */ - DataFlow::Node asSink() { Impl::def(this, result) } + bindingset[this] + pragma[inline_late] + DataFlow::Node asSink() { result = asSinkInline(this) } /** - * Get a data-flow node that transitively flows to an external library (or in general, any external codebase). + * Get a data-flow node that transitively flows to this value, provided that this value corresponds + * to a sink. * * This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow. * See `asSink()` for examples. */ - DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) } + bindingset[this] + pragma[inline_late] + DataFlow::Node getAValueReachingSink() { result = getAValueReachingSinkInline(this) } + + /** + * Gets a module or class referred to by this API node. + * + * For example: + * ```ruby + * module Foo + * class Bar # API::getTopLevelMember("Foo").getMember("Bar").asModule() + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::ModuleNode asModule() { this = Impl::MkModuleObjectDown(result) } + + /** + * Gets the call referred to by this API node. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar").asCall() + * Foo.bar + * + * class Bar < Foo + * def doSomething + * # API::getTopLevelMember("Foo").getInstance().getMethod("baz").asCall() + * baz + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::CallNode asCall() { this = Impl::MkMethodAccessNode(result) } + + /** + * DEPRECATED. Use `asCall()` instead. + */ + pragma[inline] + deprecated DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } + + /** + * Gets a module that descends from the value referenced by this API node. + */ + bindingset[this] + pragma[inline_late] + DataFlow::ModuleNode getADescendentModule() { result = this.getAnEpsilonSuccessor().asModule() } + + /** + * Gets a call to a method on the receiver represented by this API node. + * + * This is a shorthand for `getMethod(method).asCall()`, and thus returns a data-flow node + * rather than an API node. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getAMethodCall("bar") + * Foo.bar + * ``` + */ + pragma[inline] + DataFlow::CallNode getAMethodCall(string method) { + // This predicate is currently not 'inline_late' because 'method' can be an input or output + result = this.getMethod(method).asCall() + } + + /** + * Gets an access to the constant `m` with this value as the base of the access. + * + * For example, the constant `A::B` would be found by `API::getATopLevelMember("A").getMember("B")` + * For example: + * ```ruby + * A::B # API::getATopLevelMember("A").getMember("B") + * + * module A + * class B # API::getATopLevelMember("A").getMember("B") + * end + * end + * ``` + */ + pragma[inline] + Node getMember(string m) { + // This predicate is currently not 'inline_late' because 'm' can be an input or output + Impl::memberEdge(this.getAnEpsilonSuccessor(), m, result) + } + + /** + * Gets an access to a constant with this valeu as the base of the access. + * + * This is equivalent to `getMember(_)` but can be more efficient. + */ + bindingset[this] + pragma[inline_late] + Node getAMember() { Impl::anyMemberEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets a node that may refer to an instance of the module or class represented by this API node. + * + * This includes the following: + * - Calls to `new` on this module or class or a descendent thereof + * - References to `self` in instance methods declared in any ancestor of any descendent of this module or class + * + * For example: + * ```ruby + * A.new # API::getTopLevelMember("A").getInstance() + * + * class B < A + * def m + * self # API::getTopLevelMember("A").getInstance() + * end + * end + * + * B.new # API::getTopLevelMember("A").getInstance() + * + * class C < A + * include Mixin + * end + * module Mixin + * def m + * # Although 'Mixin' is not directly related to 'A', 'self' may refer to an instance of 'A' + * # due to its inclusion in a subclass of 'A'. + * self # API::getTopLevelMember("A").getInstance() + * end + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getInstance() { Impl::instanceEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets a call to `method` with this value as the receiver, or the definition of `method` on + * an object that can reach this sink. + * + * If the receiver represents a module or class object, this includes calls on descendents of that module or class. + * + * For example: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar") + * Foo.bar + * + * # API::getTopLevelMember("Foo").getInstance().getMethod("bar") + * Foo.new.bar + * + * class B < Foo + * end + * B.bar # API::getTopLevelMember("Foo").getMethod("bar") + * + * class C + * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m") + * end + * end + * Foo.bar(C.new) + * ``` + */ + pragma[inline] + Node getMethod(string method) { + // TODO: Consider 'getMethodTarget(method)' for looking up method definitions? + // This predicate is currently not 'inline_late' because 'method' can be an input or output + Impl::methodEdge(this.getAnEpsilonSuccessor(), method, result) + } + + /** + * Gets the result of this call, or the return value of this callable. + */ + bindingset[this] + pragma[inline_late] + Node getReturn() { Impl::returnEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the result of a call to `method` with this value as the receiver, or the return value of `method` defined on + * an object that can reach this sink. + * + * This is a shorthand for `getMethod(method).getReturn()`. + */ + pragma[inline] + Node getReturn(string method) { + // This predicate is currently not 'inline_late' because 'method' can be an input or output + result = this.getMethod(method).getReturn() + } + + /** + * Gets the `n`th positional argument to this call. + * + * For example, this would get `x` in the following snippet: + * ```ruby + * Foo.bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0) + * ``` + */ + pragma[inline] + Node getArgument(int n) { + // This predicate is currently not 'inline_late' because 'n' can be an input or output + Impl::positionalArgumentEdge(this, n, result) + } + + /** + * Gets the given keyword argument to this call. + * + * For example, this would get `x` in the following snippet: + * ```ruby + * Foo.bar(baz: x) # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("baz") + * ``` + */ + pragma[inline] + Node getKeywordArgument(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::keywordArgumentEdge(this, name, result) + } + + /** + * Gets the block parameter of a callable that can reach this sink. + * + * For example, this would get the `&blk` in the following snippet: + * ```ruby + * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getBlockParameter() + * Foo.bar(->(&blk) {}) + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getBlockParameter() { Impl::blockParameterEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the `n`th positional parameter of this callable, or the `n`th positional argument to this call. + * + * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. + * When referring to an argument, it is recommended to use `getArgument(n)` instead. + */ + pragma[inline] + Node getParameter(int n) { + // This predicate is currently not 'inline_late' because 'n' can be an input or output + Impl::positionalParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), n, result) + } + + /** + * Gets the given keyword parameter of this callable, or keyword argument to this call. + * + * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. + * When referring to an argument, it is recommended to use `getKeywordArgument(n)` instead. + */ + pragma[inline] + Node getKeywordParameter(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::keywordParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), name, result) + } + + /** + * Gets the block argument to this call, or the block parameter of this callable. + * + * Note: this predicate may refer to either an argument or a parameter. When referring to a block parameter, + * it is recommended to use `getBlockParameter()` instead. + * + * For example: + * ```ruby + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0) + * end + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getBlock() { Impl::blockParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the argument passed in argument position `pos` at this call. + */ + pragma[inline] + Node getArgumentAtPosition(DataFlowDispatch::ArgumentPosition pos) { + // This predicate is currently not 'inline_late' because 'pos' can be an input or output + Impl::argumentEdge(pragma[only_bind_out](this), pos, result) // note: no need for epsilon step since 'this' must be a call + } + + /** + * Gets the parameter at position `pos` of this callable. + */ + pragma[inline] + Node getParameterAtPosition(DataFlowDispatch::ParameterPosition pos) { + // This predicate is currently not 'inline_late' because 'pos' can be an input or output + Impl::parameterEdge(this.getAnEpsilonSuccessor(), pos, result) + } + + /** + * Gets a `new` call with this value as the receiver. + */ + bindingset[this] + pragma[inline_late] + DataFlow::ExprNode getAnInstantiation() { result = this.getReturn("new").asSource() } + + /** + * Gets a representative for the `content` of this value. + * + * When possible, it is preferrable to use one of the specialized variants of this predicate, such as `getAnElement`. + * + * Concretely, this gets sources where `content` is read from this value, and as well as sinks where + * `content` is stored onto this value or onto an object that can reach this sink. + */ + pragma[inline] + Node getContent(DataFlow::Content content) { + // This predicate is currently not 'inline_late' because 'content' can be an input or output + Impl::contentEdge(this.getAnEpsilonSuccessor(), content, result) + } + + /** + * Gets a representative for the `contents` of this value. + * + * See `getContent()` for more details. + */ + bindingset[this, contents] + pragma[inline_late] + Node getContents(DataFlow::ContentSet contents) { + // We always use getAStoreContent when generating content edges, and we always use getAReadContent when querying the graph. + result = this.getContent(contents.getAReadContent()) + } + + /** + * Gets a representative for the instance field of the given `name`, which must include the `@` character. + * + * This can be used to find cases where a class accesses the fields used by a base class. + * + * ```ruby + * class A < B + * def m + * @foo # API::getTopLevelMember("B").getInstance().getField("@foo") + * end + * end + * ``` + */ + pragma[inline] + Node getField(string name) { + // This predicate is currently not 'inline_late' because 'name' can be an input or output + Impl::fieldEdge(this.getAnEpsilonSuccessor(), name, result) + } + + /** + * Gets a representative for an arbitrary element of this collection. + * + * For example: + * ```ruby + * Foo.bar.each do |x| # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() + * end + * + * Foo.bar[0] # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() + * ``` + */ + bindingset[this] + pragma[inline_late] + Node getAnElement() { Impl::elementEdge(this.getAnEpsilonSuccessor(), result) } + + /** + * Gets the data-flow node that gives rise to this node, if any. + */ + DataFlow::Node getInducingNode() { + this = Impl::MkMethodAccessNode(result) or + this = Impl::MkBackwardNode(result, _) or + this = Impl::MkForwardNode(result, _) or + this = Impl::MkSinkNode(result) + } + + /** Gets the location of this node. */ + Location getLocation() { + result = this.getInducingNode().getLocation() + or + exists(DataFlow::ModuleNode mod | + this = Impl::MkModuleObjectDown(mod) + or + this = Impl::MkModuleInstanceUp(mod) + | + result = mod.getLocation() + ) + or + this instanceof RootNode and + result instanceof EmptyLocation + } + + /** + * Gets a textual representation of this element. + */ + string toString() { none() } + + /** + * Gets a node representing a (direct or indirect) subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. + */ + pragma[inline] + deprecated Node getASubclass() { result = this } + + /** + * Gets a node representing a direct subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. + */ + pragma[inline] + deprecated Node getAnImmediateSubclass() { + result = this.asModule().getAnImmediateDescendent().trackModule() + } /** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource()`. */ deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() } @@ -166,326 +629,143 @@ module API { deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() } /** - * Gets a call to a method on the receiver represented by this API component. - */ - pragma[inline] - DataFlow::CallNode getAMethodCall(string method) { result = this.getReturn(method).asSource() } - - /** - * Gets a node representing member `m` of this API component. + * DEPRECATED. API graph nodes are no longer associated with specific paths. * - * For example, a member can be: - * - * - A submodule of a module - * - An attribute of an object - */ - pragma[inline] - Node getMember(string m) { - result = pragma[only_bind_out](this).(Node::Internal).getMemberInternal(m) - } - - /** - * Gets a node representing a member of this API component where the name of the member may - * or may not be known statically. - */ - cached - Node getAMember() { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::member(_)) - } - - /** - * Gets a node representing an instance of this API component, that is, an object whose - * constructor is the function represented by this node. - * - * For example, if this node represents a use of some class `A`, then there might be a node - * representing instances of `A`, typically corresponding to expressions `A.new` at the - * source level. - * - * This predicate may have multiple results when there are multiple constructor calls invoking this API component. - * Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls. - */ - pragma[inline] - Node getInstance() { result = this.getASubclass().getReturn("new") } - - /** - * Gets a node representing a call to `method` on the receiver represented by this node. - */ - pragma[inline] - MethodAccessNode getMethod(string method) { - result = pragma[only_bind_out](this).(Node::Internal).getMethodInternal(method) - } - - /** - * Gets a node representing the result of this call. - */ - pragma[inline] - Node getReturn() { result = pragma[only_bind_out](this).(Node::Internal).getReturnInternal() } - - /** - * Gets a node representing the result of calling a method on the receiver represented by this node. - */ - pragma[inline] - Node getReturn(string method) { result = this.getMethod(method).getReturn() } - - /** Gets an API node representing the `n`th positional parameter. */ - cached - Node getParameter(int n) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::parameter(n)) - } - - /** Gets an API node representing the given keyword parameter. */ - cached - Node getKeywordParameter(string name) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::keywordParameter(name)) - } - - /** Gets an API node representing the block parameter. */ - cached - Node getBlock() { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::blockParameter()) - } - - /** - * Gets a `new` call to the function represented by this API component. - */ - pragma[inline] - DataFlow::ExprNode getAnInstantiation() { result = this.getInstance().asSource() } - - /** - * Gets a node representing a (direct or indirect) subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. - */ - Node getASubclass() { result = this.getAnImmediateSubclass*() } - - /** - * Gets a node representing a direct subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. - */ - cached - Node getAnImmediateSubclass() { - Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::subclass()) - } - - /** - * Gets a node representing the `content` stored on the base object. - */ - cached - Node getContent(DataFlow::Content content) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::content(content)) - } - - /** - * Gets a node representing the `contents` stored on the base object. - */ - pragma[inline] - Node getContents(DataFlow::ContentSet contents) { - // We always use getAStoreContent when generating the graph, and we always use getAReadContent when querying the graph. - result = this.getContent(contents.getAReadContent()) - } - - /** Gets a node representing the instance field of the given `name`, which must include the `@` character. */ - cached - Node getField(string name) { - Impl::forceCachingInSameStage() and - result = this.getContent(DataFlowPrivate::TFieldContent(name)) - } - - /** Gets a node representing an element of this collection (known or unknown). */ - cached - Node getAnElement() { - Impl::forceCachingInSameStage() and - result = this.getContents(any(DataFlow::ContentSet set | set.isAnyElement())) - } - - /** * Gets a string representation of the lexicographically least among all shortest access paths * from the root to this node. */ - string getPath() { - result = min(string p | p = this.getAPath(Impl::distanceFromRoot(this)) | p) - } + deprecated string getPath() { none() } /** + * DEPRECATED. Use label-specific predicates in this class, such as `getMember`, instead of using `getASuccessor`. + * * Gets a node such that there is an edge in the API graph between this node and the other * one, and that edge is labeled with `lbl`. */ - Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) } + pragma[inline] + deprecated Node getASuccessor(Label::ApiLabel lbl) { + labelledEdge(this.getAnEpsilonSuccessor(), lbl, result) + } /** + * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get + * a node intended for backtracking. + * * Gets a node such that there is an edge in the API graph between that other node and * this one, and that edge is labeled with `lbl` */ - Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } + deprecated Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } /** + * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get + * a node intended for backtracking. + * * Gets a node such that there is an edge in the API graph between this node and the other * one. */ - Node getAPredecessor() { result = this.getAPredecessor(_) } + deprecated Node getAPredecessor() { result = this.getAPredecessor(_) } /** * Gets a node such that there is an edge in the API graph between that other node and * this one. */ - Node getASuccessor() { result = this.getASuccessor(_) } + pragma[inline] + deprecated Node getASuccessor() { result = this.getASuccessor(_) } - /** - * Gets the data-flow node that gives rise to this node, if any. - */ - DataFlow::Node getInducingNode() { - this = Impl::MkUse(result) - or - this = Impl::MkDef(result) - or - this = Impl::MkMethodAccessNode(result) - } + /** DEPRECATED. API graphs are no longer associated with a depth. */ + deprecated int getDepth() { none() } - /** Gets the location of this node. */ - Location getLocation() { - result = this.getInducingNode().getLocation() - or - exists(DataFlow::ModuleNode mod | - this = Impl::MkModuleObject(mod) and - result = mod.getLocation() - ) - or - // For nodes that do not have a meaningful location, `path` is the empty string and all other - // parameters are zero. - not exists(this.getInducingNode()) and - result instanceof EmptyLocation - } - - /** - * Gets a textual representation of this element. - */ - string toString() { none() } - - /** - * Gets a path of the given `length` from the root to this node. - */ - private string getAPath(int length) { - this instanceof Impl::MkRoot and - length = 0 and - result = "" - or - exists(Node pred, Label::ApiLabel lbl, string predpath | - Impl::edge(pred, lbl, this) and - predpath = pred.getAPath(length - 1) and - exists(string dot | if length = 1 then dot = "" else dot = "." | - result = predpath + dot + lbl and - // avoid producing strings longer than 1MB - result.length() < 1000 * 1000 - ) - ) and - length in [1 .. Impl::distanceFromRoot(this)] - } - - /** Gets the shortest distance from the root to this node in the API graph. */ - int getDepth() { result = Impl::distanceFromRoot(this) } + pragma[inline] + private Node getAnEpsilonSuccessor() { result = getAnEpsilonSuccessorInline(this) } } - /** Companion module to the `Node` class. */ - module Node { - /** - * INTERNAL USE ONLY. - * - * An API node, with some internal predicates exposed. - */ - class Internal extends Node { - /** - * INTERNAL USE ONLY. - * - * Same as `asSource()` but without join-order hints. - */ - cached - DataFlow::LocalSourceNode asSourceInternal() { - Impl::forceCachingInSameStage() and - Impl::use(this, result) - } + /** DEPRECATED. Use `API::root()` to access the root node. */ + deprecated class Root = RootNode; - /** - * Same as `getMember` but without join-order hints. - */ - cached - Node getMemberInternal(string m) { - Impl::forceCachingInSameStage() and - result = this.getASuccessor(Label::member(m)) - } + /** DEPRECATED. A node corresponding to the use of an API component. */ + deprecated class Use = ForwardNode; - /** - * Same as `getMethod` but without join-order hints. - */ - cached - MethodAccessNode getMethodInternal(string method) { - Impl::forceCachingInSameStage() and - result = this.getASubclass().getASuccessor(Label::method(method)) - } - - /** - * INTERNAL USE ONLY. - * - * Same as `getReturn()` but without join-order hints. - */ - cached - Node getReturnInternal() { - Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::return()) - } - } - } - - bindingset[node] - pragma[inline_late] - private DataFlow::Node getAValueReachableFromSourceInline(Node node) { - exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode dst | - Impl::use(node, pragma[only_bind_into](src)) and - pragma[only_bind_into](dst) = Impl::trackUseNode(src) and - dst.flowsTo(result) - ) - } + /** DEPRECATED. A node corresponding to a value escaping into an API component. */ + deprecated class Def = SinkNode; /** The root node of an API graph. */ - class Root extends Node, Impl::MkRoot { - override string toString() { result = "root" } + private class RootNode extends Node, Impl::MkRoot { + override string toString() { result = "Root()" } } - private string tryGetPath(Node node) { - result = node.getPath() - or - not exists(node.getPath()) and - result = "with no path" + /** A node representing a given type-tracking state when tracking forwards. */ + private class ForwardNode extends Node, Impl::MkForwardNode { + private DataFlow::LocalSourceNode node; + private TypeTracker tracker; + + ForwardNode() { this = Impl::MkForwardNode(node, tracker) } + + override string toString() { + if tracker.start() + then result = "ForwardNode(" + node + ")" + else result = "ForwardNode(" + node + ", " + tracker + ")" + } } - /** A node corresponding to the use of an API component. */ - class Use extends Node, Impl::MkUse { - override string toString() { result = "Use " + tryGetPath(this) } + /** A node representing a given type-tracking state when tracking backwards. */ + private class BackwardNode extends Node, Impl::MkBackwardNode { + private DataFlow::LocalSourceNode node; + private TypeTracker tracker; + + BackwardNode() { this = Impl::MkBackwardNode(node, tracker) } + + override string toString() { + if tracker.start() + then result = "BackwardNode(" + node + ")" + else result = "BackwardNode(" + node + ", " + tracker + ")" + } } - /** A node corresponding to a value escaping into an API component. */ - class Def extends Node, Impl::MkDef { - override string toString() { result = "Def " + tryGetPath(this) } + /** A node representing a module/class object with epsilon edges to its descendents. */ + private class ModuleObjectDownNode extends Node, Impl::MkModuleObjectDown { + /** Gets the module represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectDown(result) } + + override string toString() { result = "ModuleObjectDown(" + this.getModule() + ")" } + } + + /** A node representing a module/class object with epsilon edges to its ancestors. */ + private class ModuleObjectUpNode extends Node, Impl::MkModuleObjectUp { + /** Gets the module represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectUp(result) } + + override string toString() { result = "ModuleObjectUp(" + this.getModule() + ")" } + } + + /** A node representing instances of a module/class with epsilon edges to its ancestors. */ + private class ModuleInstanceUpNode extends Node, Impl::MkModuleInstanceUp { + /** Gets the module whose instances are represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceUp(result) } + + override string toString() { result = "ModuleInstanceUp(" + this.getModule() + ")" } + } + + /** A node representing instances of a module/class with epsilon edges to its descendents. */ + private class ModuleInstanceDownNode extends Node, Impl::MkModuleInstanceDown { + /** Gets the module whose instances are represented by this API node. */ + DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceDown(result) } + + override string toString() { result = "ModuleInstanceDown(" + this.getModule() + ")" } } /** A node corresponding to the method being invoked at a method call. */ class MethodAccessNode extends Node, Impl::MkMethodAccessNode { - override string toString() { result = "MethodAccessNode " + tryGetPath(this) } + override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" } + } - /** Gets the call node corresponding to this method access. */ - DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } + /** + * A node corresponding to an argument, right-hand side of a store, or return value from a callable. + * + * Such a node may serve as the starting-point of backtracking, and has epsilon edges going + * the backward nodes corresponding to `getALocalSource`. + */ + private class SinkNode extends Node, Impl::MkSinkNode { + override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" } } /** @@ -499,6 +779,8 @@ module API { * additional entry points may be added by extending this class. */ abstract class EntryPoint extends string { + // Note: this class can be deprecated in Ruby, but is still referenced by shared code in ApiGraphModels.qll, + // where it can't be removed since other languages are still dependent on the EntryPoint class. bindingset[this] EntryPoint() { any() } @@ -518,7 +800,7 @@ module API { DataFlow::CallNode getACall() { none() } /** Gets an API-node for this entry point. */ - API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } + API::Node getANode() { Impl::entryPointEdge(this, result) } } // Ensure all entry points are imported from ApiGraphs.qll @@ -527,88 +809,301 @@ module API { } /** Gets the root node. */ - Root root() { any() } + Node root() { result instanceof RootNode } /** - * Gets a node corresponding to a top-level member `m` (typically a module). + * Gets an access to the top-level constant `name`. * - * This is equivalent to `root().getAMember("m")`. - * - * Note: You should only use this predicate for top level modules or classes. If you want nodes corresponding to a nested module or class, - * you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`, + * To access nested constants, use `getMember()` on the resulting node. For example, for nodes corresponding to the class `Gem::Version`, * use `getTopLevelMember("Gem").getMember("Version")`. */ - cached - Node getTopLevelMember(string m) { - Impl::forceCachingInSameStage() and result = root().(Node::Internal).getMemberInternal(m) - } + pragma[inline] + Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) } /** - * Provides the actual implementation of API graphs, cached for performance. - * - * Ideally, we'd like nodes to correspond to (global) access paths, with edge labels - * corresponding to extending the access path by one element. We also want to be able to map - * nodes to their definitions and uses in the data-flow graph, and this should happen modulo - * (inter-procedural) data flow. - * - * This, however, is not easy to implement, since access paths can have unbounded length - * and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing - * a condition like "this node hasn't been involved in constructing any predecessor of - * this node in the API graph" without negative recursion is tricky. - * - * So instead most nodes are directly associated with a data-flow node, representing - * either a use or a definition of an API component. This ensures that we only have a finite - * number of nodes. However, we can now have multiple nodes with the same access - * path, which are essentially indistinguishable for a client of the API. - * - * On the other hand, a single node can have multiple access paths (which is, of - * course, unavoidable). We pick as canonical the alphabetically least access path with - * shortest length. + * Gets an unqualified call at the top-level with the given method name. */ - cached - private module Impl { - cached - predicate forceCachingInSameStage() { any() } + pragma[inline] + MethodAccessNode getTopLevelCall(string name) { Impl::toplevelCall(name, result) } - cached - predicate forceCachingBackref() { - 1 = 1 + pragma[nomagic] + private predicate isReachable(DataFlow::LocalSourceNode node, TypeTracker t) { + t.start() and exists(node) + or + exists(DataFlow::LocalSourceNode prev, TypeTracker t2 | + isReachable(prev, t2) and + node = prev.track(t2, t) and + notSelfParameter(node) + ) + } + + bindingset[node] + pragma[inline_late] + private predicate notSelfParameter(DataFlow::Node node) { + not node instanceof DataFlow::SelfParameterNode + } + + private module SharedArg implements ApiGraphSharedSig { + class ApiNode = Node; + + ApiNode getForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { + result = Impl::MkForwardNode(node, t) + } + + ApiNode getBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { + result = Impl::MkBackwardNode(node, t) + } + + ApiNode getSinkNode(DataFlow::Node node) { result = Impl::MkSinkNode(node) } + + pragma[nomagic] + predicate specificEpsilonEdge(ApiNode pred, ApiNode succ) { + exists(DataFlow::ModuleNode mod | + moduleReferenceEdge(mod, pred, succ) + or + moduleInheritanceEdge(mod, pred, succ) + or + pred = getForwardEndNode(getSuperClassNode(mod)) and + succ = Impl::MkModuleObjectDown(mod) + ) or - exists(getTopLevelMember(_)) + implicitCallEdge(pred, succ) or - exists( - any(Node n) - .(Node::Internal) - .getMemberInternal("foo") - .getAMember() - .(Node::Internal) - .getMethodInternal("foo") - .(Node::Internal) - .getReturnInternal() - .getParameter(0) - .getKeywordParameter("foo") - .getBlock() - .getAnImmediateSubclass() - .getContent(_) - .getField(_) - .getAnElement() - .(Node::Internal) - .asSourceInternal() + exists(DataFlow::HashLiteralNode splat | hashSplatEdge(splat, pred, succ)) + } + + /** + * Holds if the epsilon edge `pred -> succ` should be generated, to handle inheritance relations of `mod`. + */ + pragma[inline] + private predicate moduleInheritanceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { + pred = Impl::MkModuleObjectDown(mod) and + succ = Impl::MkModuleObjectDown(mod.getAnImmediateDescendent()) + or + pred = Impl::MkModuleInstanceDown(mod) and + succ = Impl::MkModuleInstanceDown(mod.getAnImmediateDescendent()) + or + exists(DataFlow::ModuleNode ancestor | + ancestor = mod.getAnImmediateAncestor() and + // Restrict flow back to Object to avoid spurious flow for methods that happen + // to exist on Object, such as top-level methods. + not ancestor.getQualifiedName() = "Object" + | + pred = Impl::MkModuleInstanceUp(mod) and + succ = Impl::MkModuleInstanceUp(ancestor) + or + pred = Impl::MkModuleObjectUp(mod) and + succ = Impl::MkModuleObjectUp(ancestor) + ) + or + // Due to multiple inheritance, allow upwards traversal after downward traversal, + // so we can detect calls sideways in the hierarchy. + // Note that a similar case does not exist for ModuleObject since singleton methods are only inherited + // from the superclass, and there can only be one superclass. + pred = Impl::MkModuleInstanceDown(mod) and + succ = Impl::MkModuleInstanceUp(mod) + } + + /** + * Holds if the epsilon `pred -> succ` be generated, to associate `mod` with its references in the codebase. + */ + bindingset[mod] + pragma[inline_late] + private predicate moduleReferenceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { + pred = Impl::MkModuleObjectDown(mod) and + succ = getForwardStartNode(getAModuleReference(mod)) + or + pred = getBackwardEndNode(getAModuleReference(mod)) and + ( + succ = Impl::MkModuleObjectUp(mod) + or + succ = Impl::MkModuleObjectDown(mod) + ) + or + pred = Impl::MkModuleInstanceUp(mod) and + succ = getAModuleInstanceUseNode(mod) + or + pred = getAModuleInstanceDefNode(mod) and + succ = Impl::MkModuleInstanceUp(mod) + or + pred = getAModuleDescendentInstanceDefNode(mod) and + succ = Impl::MkModuleInstanceDown(mod) + } + + /** + * Holds if the epsilon step `pred -> succ` should be generated to account for the fact that `getMethod("call")` + * may be omitted when dealing with blocks, lambda, or procs. + * + * For example, a block may be invoked by a `yield`, or can be converted to a proc and then invoked via `.call`. + * To simplify this, the implicit proc conversion is seen as a no-op and the `.call` is omitted. + */ + pragma[nomagic] + private predicate implicitCallEdge(ApiNode pred, ApiNode succ) { + // Step from &block parameter to yield call without needing `getMethod("call")`. + exists(DataFlow::MethodNode method | + pred = getForwardEndNode(method.getBlockParameter()) and + succ = Impl::MkMethodAccessNode(method.getABlockCall()) + ) + or + // Step from x -> x.call (the call itself, not its return value), without needing `getMethod("call")`. + exists(DataFlow::CallNode call | + call.getMethodName() = "call" and + pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and + succ = Impl::MkMethodAccessNode(call) + ) + or + exists(DataFlow::ModuleNode mod | + // Step from module/class to its own `call` method without needing `getMethod("call")`. + (pred = Impl::MkModuleObjectDown(mod) or pred = Impl::MkModuleObjectUp(mod)) and + succ = getBackwardEndNode(mod.getOwnSingletonMethod("call")) + or + pred = Impl::MkModuleInstanceUp(mod) and + succ = getBackwardEndNode(mod.getOwnInstanceMethod("call")) ) } + pragma[nomagic] + private DataFlow::Node getHashSplatArgument(DataFlow::HashLiteralNode literal) { + result = DataFlowPrivate::TSynthHashSplatArgumentNode(literal.asExpr()) + } + + /** + * Holds if the epsilon edge `pred -> succ` should be generated to account for the members of a hash literal. + * + * This currently exists because hash literals are desugared to `Hash.[]` calls, whose summary relies on `WithContent`. + * However, `contentEdge` does not currently generate edges for `WithContent` steps. + */ + bindingset[literal] + pragma[inline_late] + private predicate hashSplatEdge(DataFlow::HashLiteralNode literal, ApiNode pred, ApiNode succ) { + exists(TypeTracker t | + pred = Impl::MkForwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and + succ = Impl::MkForwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) + or + succ = Impl::MkBackwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and + pred = Impl::MkBackwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) + ) + } + + pragma[nomagic] + private DataFlow::LocalSourceNode getAModuleReference(DataFlow::ModuleNode mod) { + result = mod.getAnImmediateReference() + or + mod.getAnAncestor().getAnOwnInstanceSelf() = getANodeReachingClassCall(result) + } + + /** + * Gets an API node that may refer to an instance of `mod`. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleInstanceUseNode(DataFlow::ModuleNode mod) { + result = getForwardStartNode(mod.getAnOwnInstanceSelf()) + } + + /** + * Gets a node that can be backtracked to an instance of `mod`. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleInstanceDefNode(DataFlow::ModuleNode mod) { + result = getBackwardEndNode(mod.getAnImmediateReference().getAMethodCall("new")) + } + + /** + * Gets a node that can be backtracked to an instance of `mod` or any of its descendents. + */ + bindingset[mod] + pragma[inline_late] + private ApiNode getAModuleDescendentInstanceDefNode(DataFlow::ModuleNode mod) { + result = getBackwardEndNode(mod.getAnOwnInstanceSelf()) + } + + /** + * Holds if `superclass` is the superclass of `mod`. + */ + pragma[nomagic] + private DataFlow::LocalSourceNode getSuperClassNode(DataFlow::ModuleNode mod) { + result.getALocalUse().asExpr().getExpr() = + mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() + } + + /** Gets a node that can reach the receiver of the given `.class` call. */ + private DataFlow::LocalSourceNode getANodeReachingClassCall( + DataFlow::CallNode call, TypeBackTracker t + ) { + t.start() and + call.getMethodName() = "class" and + result = getALocalSourceStrict(call.getReceiver()) + or + exists(DataFlow::LocalSourceNode prev, TypeBackTracker t2 | + prev = getANodeReachingClassCall(call, t2) and + result = prev.backtrack(t2, t) and + notSelfParameter(prev) + ) + } + + /** Gets a node that can reach the receiver of the given `.class` call. */ + private DataFlow::LocalSourceNode getANodeReachingClassCall(DataFlow::CallNode call) { + result = getANodeReachingClassCall(call, TypeBackTracker::end()) + } + } + + /** INTERNAL USE ONLY. */ + module Internal { + private module Shared = ApiGraphShared; + + import Shared + + /** Gets the API node corresponding to the module/class object for `mod`. */ + bindingset[mod] + pragma[inline_late] + Node getModuleNode(DataFlow::ModuleNode mod) { result = Impl::MkModuleObjectDown(mod) } + + /** Gets the API node corresponding to instances of `mod`. */ + bindingset[mod] + pragma[inline_late] + Node getModuleInstance(DataFlow::ModuleNode mod) { result = getModuleNode(mod).getInstance() } + } + + private import Internal + import Internal::Public + + cached + private module Impl { cached newtype TApiNode = /** The root of the API graph. */ MkRoot() or /** The method accessed at `call`, synthetically treated as a separate object. */ - MkMethodAccessNode(DataFlow::CallNode call) { isUse(call) } or - /** A use of an API member at the node `nd`. */ - MkUse(DataFlow::Node nd) { isUse(nd) } or - /** A value that escapes into an external library at the node `nd` */ - MkDef(DataFlow::Node nd) { isDef(nd) } or - /** A module object seen as a use node. */ - MkModuleObject(DataFlow::ModuleNode mod) + MkMethodAccessNode(DataFlow::CallNode call) or + /** The module object `mod` with epsilon edges to its ancestors. */ + MkModuleObjectUp(DataFlow::ModuleNode mod) or + /** The module object `mod` with epsilon edges to its descendents. */ + MkModuleObjectDown(DataFlow::ModuleNode mod) or + /** Instances of `mod` with epsilon edges to its ancestors. */ + MkModuleInstanceUp(DataFlow::ModuleNode mod) or + /** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */ + MkModuleInstanceDown(DataFlow::ModuleNode mod) or + /** Intermediate node for following forward data flow. */ + MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + /** Intermediate node for following backward data flow. */ + MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + MkSinkNode(DataFlow::Node node) { needsSinkNode(node) } + + private predicate needsSinkNode(DataFlow::Node node) { + node instanceof DataFlowPrivate::ArgumentNode + or + TypeTrackerSpecific::basicStoreStep(node, _, _) + or + node = any(DataFlow::CallableNode callable).getAReturnNode() + or + node = any(EntryPoint e).getASink() + } + + bindingset[e] + pragma[inline_late] + private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e } private string resolveTopLevel(ConstantReadAccess read) { result = read.getModule().getQualifiedName() and @@ -616,300 +1111,283 @@ module API { } /** - * Holds if `ref` is a use of a node that should have an incoming edge from the root - * node labeled `lbl` in the API graph (not including those from API::EntryPoint). + * Holds `pred` should have a member edge to `mod`. */ pragma[nomagic] - private predicate useRoot(Label::ApiLabel lbl, DataFlow::Node ref) { - exists(string name, ConstantReadAccess read | - read = ref.asExpr().getExpr() and - lbl = Label::member(read.getName()) + private predicate moduleScope(DataFlow::ModuleNode mod, Node pred, string name) { + exists(Namespace namespace | + name = namespace.getName() and + namespace = mod.getADeclaration() | - name = resolveTopLevel(read) + exists(DataFlow::Node scopeNode | + scopeNode.asExpr().getExpr() = namespace.getScopeExpr() and + pred = getForwardEndNode(getALocalSourceStrict(scopeNode)) + ) or - name = read.getName() and - not exists(resolveTopLevel(read)) and - not exists(read.getScopeExpr()) + not exists(namespace.getScopeExpr()) and + if namespace.hasGlobalScope() or namespace.getEnclosingModule() instanceof Toplevel + then pred = MkRoot() + else pred = MkModuleObjectDown(namespace.getEnclosingModule().getModule()) ) } - /** - * Holds if `ref` is a use of a node that should have an incoming edge labeled `lbl`, - * from a use node that flows to `node`. - */ - private predicate useStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node ref) { - // // Referring to an attribute on a node that is a use of `base`: - // pred = `Rails` part of `Rails::Whatever` - // lbl = `Whatever` - // ref = `Rails::Whatever` - exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read | - not exists(resolveTopLevel(read)) and - node.asExpr() = c.getScopeExpr() and - lbl = Label::member(read.getName()) and - ref.asExpr() = c and - read = c.getExpr() - ) - or - exists(TypeTrackerSpecific::TypeTrackerContent c | - TypeTrackerSpecific::basicLoadStep(node, ref, c) and - lbl = Label::content(c.getAStoreContent()) and - not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) - ) - // note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node - } - - /** - * Holds if `rhs` is a definition of a node that should have an incoming edge labeled `lbl`, - * from a def node that is reachable from `node`. - */ - private predicate defStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node rhs) { - exists(TypeTrackerSpecific::TypeTrackerContent c | - TypeTrackerSpecific::basicStoreStep(rhs, node, c) and - lbl = Label::content(c.getAStoreContent()) - ) - } - - pragma[nomagic] - private predicate isUse(DataFlow::Node nd) { - useRoot(_, nd) - or - exists(DataFlow::Node node | - useCandFwd().flowsTo(node) and - useStep(_, node, nd) - ) - or - useCandFwd().flowsTo(nd.(DataFlow::CallNode).getReceiver()) - or - parameterStep(_, defCand(), nd) - or - nd = any(EntryPoint entry).getASource() - or - nd = any(EntryPoint entry).getACall() - } - - /** - * Holds if `ref` is a use of node `nd`. - */ cached - predicate use(TApiNode nd, DataFlow::Node ref) { - nd = MkUse(ref) + predicate memberEdge(Node pred, string name, Node succ) { + exists(ConstantReadAccess read | succ = getForwardStartNode(getNodeFromExpr(read)) | + name = resolveTopLevel(read) and + pred = MkRoot() + or + not exists(resolveTopLevel(read)) and + not exists(read.getScopeExpr()) and + name = read.getName() and + pred = MkRoot() + or + pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(read.getScopeExpr()))) and + name = read.getName() + ) or exists(DataFlow::ModuleNode mod | - nd = MkModuleObject(mod) and - ref = mod.getAnImmediateReference() + moduleScope(mod, pred, name) and + (succ = MkModuleObjectDown(mod) or succ = MkModuleObjectUp(mod)) ) } - /** - * Holds if `rhs` is a RHS of node `nd`. - */ cached - predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) } + predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) } - /** Gets a node reachable from a use-node. */ - private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) { - t.start() and - isUse(result) - or - exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t)) - } - - /** Gets a node reachable from a use-node. */ - private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) } - - private predicate isDef(DataFlow::Node rhs) { - // If a call node is relevant as a use-node, treat its arguments as def-nodes - argumentStep(_, useCandFwd(), rhs) - or - defStep(_, defCand(), rhs) - or - rhs = any(EntryPoint entry).getASink() - } - - /** Gets a data flow node that flows to the RHS of a def-node. */ - private DataFlow::LocalSourceNode defCand(TypeBackTracker t) { - t.start() and - exists(DataFlow::Node rhs | - isDef(rhs) and - result = rhs.getALocalSource() - ) - or - exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t)) - } - - /** Gets a data flow node that flows to the RHS of a def-node. */ - private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) } - - /** - * Holds if there should be a `lbl`-edge from the given call to an argument. - */ - pragma[nomagic] - private predicate argumentStep( - Label::ApiLabel lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument - ) { - exists(DataFlowDispatch::ArgumentPosition argPos | - argument.sourceArgumentOf(call.asExpr(), argPos) and - lbl = Label::getLabelFromArgumentPosition(argPos) - ) - } - - /** - * Holds if there should be a `lbl`-edge from the given callable to a parameter. - */ - pragma[nomagic] - private predicate parameterStep( - Label::ApiLabel lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode - ) { - exists(DataFlowDispatch::ParameterPosition paramPos | - paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and - lbl = Label::getLabelFromParameterPosition(paramPos) - ) - } - - /** - * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. - * - * The flow from `src` to the returned node may be inter-procedural. - */ - private DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) { - result = src and - isUse(src) and - t.start() - or - exists(TypeTracker t2 | - result = trackUseNode(src, t2).track(t2, t) and - not result instanceof DataFlow::SelfParameterNode - ) - } - - /** - * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. - * - * The flow from `src` to the returned node may be inter-procedural. - */ cached - DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) { - result = trackUseNode(src, TypeTracker::end()) - } - - /** Gets a data flow node reaching the RHS of the given def node. */ - private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) { - t.start() and - isDef(rhs) and - result = rhs.getALocalSource() - or - exists(TypeBackTracker t2, DataFlow::LocalSourceNode mid | - mid = trackDefNode(rhs, t2) and - not mid instanceof DataFlow::SelfParameterNode and - result = mid.backtrack(t2, t) + predicate toplevelCall(string name, Node node) { + exists(DataFlow::CallNode call | + call.asExpr().getExpr().getCfgScope() instanceof Toplevel and + call.getMethodName() = name and + node = MkMethodAccessNode(call) ) } - /** Gets a data flow node reaching the RHS of the given def node. */ cached - DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) { - result = trackDefNode(rhs, TypeBackTracker::end()) - } + predicate anyMemberEdge(Node pred, Node succ) { memberEdge(pred, _, succ) } - pragma[nomagic] - private predicate useNodeReachesReceiver(DataFlow::Node use, DataFlow::CallNode call) { - trackUseNode(use).flowsTo(call.getReceiver()) - } - - /** - * Holds if `superclass` is the superclass of `mod`. - */ - pragma[nomagic] - private predicate superclassNode(DataFlow::ModuleNode mod, DataFlow::Node superclass) { - superclass.asExpr().getExpr() = mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() - } - - /** - * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. - */ cached - predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) { - /* Every node that is a use of an API component is itself added to the API graph. */ - exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) | - pred = MkRoot() and - useRoot(lbl, ref) + predicate methodEdge(Node pred, string name, Node succ) { + exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | + // Treat super calls as if they were calls to the module object/instance. + succ = MkMethodAccessNode(call) and + name = call.getMethodName() + | + pred = MkModuleObjectDown(mod) and + call = mod.getAnOwnSingletonMethod().getASuperCall() or - exists(DataFlow::Node node, DataFlow::Node src | - use(pred, src) and - trackUseNode(src).flowsTo(node) and - useStep(lbl, node, ref) - ) - or - exists(DataFlow::Node callback | - def(pred, callback) and - parameterStep(lbl, trackDefNode(callback), ref) - ) - ) - or - exists(DataFlow::Node predNode, DataFlow::Node succNode | - def(pred, predNode) and - succ = MkDef(succNode) and - defStep(lbl, trackDefNode(predNode), succNode) - ) - or - exists(DataFlow::Node predNode, DataFlow::Node superclassNode, DataFlow::ModuleNode mod | - use(pred, predNode) and - trackUseNode(predNode).flowsTo(superclassNode) and - superclassNode(mod, superclassNode) and - succ = MkModuleObject(mod) and - lbl = Label::subclass() + pred = MkModuleInstanceUp(mod) and + call = mod.getAnOwnInstanceMethod().getASuperCall() ) or exists(DataFlow::CallNode call | // from receiver to method call node - exists(DataFlow::Node receiver | - use(pred, receiver) and - useNodeReachesReceiver(receiver, call) and - lbl = Label::method(call.getMethodName()) and - succ = MkMethodAccessNode(call) - ) - or - // from method call node to return and arguments - pred = MkMethodAccessNode(call) and - ( - lbl = Label::return() and - succ = MkUse(call) - or - exists(DataFlow::Node rhs | - argumentStep(lbl, call, rhs) and - succ = MkDef(rhs) - ) - ) + pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and + succ = MkMethodAccessNode(call) and + name = call.getMethodName() ) or - exists(EntryPoint entry | - pred = root() and - lbl = Label::entryPoint(entry) - | - succ = MkDef(entry.getASink()) + exists(DataFlow::ModuleNode mod | + (pred = MkModuleObjectDown(mod) or pred = MkModuleObjectUp(mod)) and + succ = getBackwardStartNode(mod.getOwnSingletonMethod(name)) or - succ = MkUse(entry.getASource()) - or - succ = MkMethodAccessNode(entry.getACall()) + pred = MkModuleInstanceUp(mod) and + succ = getBackwardStartNode(mod.getOwnInstanceMethod(name)) ) } - /** - * Holds if there is an edge from `pred` to `succ` in the API graph. - */ - private predicate edge(TApiNode pred, TApiNode succ) { edge(pred, _, succ) } - - /** Gets the shortest distance from the root to `nd` in the API graph. */ cached - int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result) + predicate contentEdge(Node pred, DataFlow::Content content, Node succ) { + exists( + DataFlow::Node object, DataFlow::Node value, TypeTrackerSpecific::TypeTrackerContent c + | + TypeTrackerSpecific::basicLoadStep(object, value, c) and + content = c.getAStoreContent() and + not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) and + // `x -> x.foo` with content "foo" + pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and + succ = getForwardStartNode(value) + or + // Based on `object.c = value` generate `object -> value` with content `c` + TypeTrackerSpecific::basicStoreStep(value, object, c) and + content = c.getAStoreContent() and + pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and + succ = MkSinkNode(value) + ) + } + cached + predicate fieldEdge(Node pred, string name, Node succ) { + Impl::contentEdge(pred, DataFlowPrivate::TFieldContent(name), succ) + } + + cached + predicate elementEdge(Node pred, Node succ) { + contentEdge(pred, any(DataFlow::ContentSet set | set.isAnyElement()).getAReadContent(), succ) + } + + cached + predicate parameterEdge(Node pred, DataFlowDispatch::ParameterPosition paramPos, Node succ) { + exists(DataFlowPrivate::ParameterNodeImpl parameter, DataFlow::CallableNode callable | + parameter.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and + pred = getBackwardEndNode(callable) and + succ = getForwardStartNode(parameter) + ) + } + + cached + predicate argumentEdge(Node pred, DataFlowDispatch::ArgumentPosition argPos, Node succ) { + exists(DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument | + argument.sourceArgumentOf(call.asExpr(), argPos) and + pred = MkMethodAccessNode(call) and + succ = MkSinkNode(argument) + ) + } + + cached + predicate positionalArgumentEdge(Node pred, int n, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isPositional(n)), succ) + } + + cached + predicate keywordArgumentEdge(Node pred, string name, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isKeyword(name)), succ) + } + + private predicate blockArgumentEdge(Node pred, Node succ) { + argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isBlock()), succ) + } + + private predicate positionalParameterEdge(Node pred, int n, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isPositional(n)), succ) + } + + private predicate keywordParameterEdge(Node pred, string name, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isKeyword(name)), succ) + } + + cached + predicate blockParameterEdge(Node pred, Node succ) { + parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isBlock()), succ) + } + + cached + predicate positionalParameterOrArgumentEdge(Node pred, int n, Node succ) { + positionalArgumentEdge(pred, n, succ) + or + positionalParameterEdge(pred, n, succ) + } + + cached + predicate keywordParameterOrArgumentEdge(Node pred, string name, Node succ) { + keywordArgumentEdge(pred, name, succ) + or + keywordParameterEdge(pred, name, succ) + } + + cached + predicate blockParameterOrArgumentEdge(Node pred, Node succ) { + blockArgumentEdge(pred, succ) + or + blockParameterEdge(pred, succ) + } + + pragma[nomagic] + private predicate newCall(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call) { + call = receiver.getAMethodCall("new") + } + + cached + predicate instanceEdge(Node pred, Node succ) { + exists(DataFlow::ModuleNode mod | + pred = MkModuleObjectDown(mod) and + succ = MkModuleInstanceUp(mod) + ) + or + exists(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call | + newCall(receiver, call) and + pred = getForwardEndNode(receiver) and + succ = getForwardStartNode(call) + ) + } + + cached + predicate returnEdge(Node pred, Node succ) { + exists(DataFlow::CallNode call | + pred = MkMethodAccessNode(call) and + succ = getForwardStartNode(call) + ) + or + exists(DataFlow::CallableNode callable | + pred = getBackwardEndNode(callable) and + succ = MkSinkNode(callable.getAReturnNode()) + ) + } + + cached + predicate entryPointEdge(EntryPoint entry, Node node) { + node = MkSinkNode(entry.getASink()) or + node = getForwardStartNode(entry.getASource()) or + node = MkMethodAccessNode(entry.getACall()) + } + } + + /** + * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. + */ + pragma[nomagic] + deprecated private predicate labelledEdge(Node pred, Label::ApiLabel lbl, Node succ) { + exists(string name | + Impl::memberEdge(pred, name, succ) and + lbl = Label::member(name) + ) + or + exists(string name | + Impl::methodEdge(pred, name, succ) and + lbl = Label::method(name) + ) + or + exists(DataFlow::Content content | + Impl::contentEdge(pred, content, succ) and + lbl = Label::content(content) + ) + or + exists(DataFlowDispatch::ParameterPosition pos | + Impl::parameterEdge(pred, pos, succ) and + lbl = Label::getLabelFromParameterPosition(pos) + ) + or + exists(DataFlowDispatch::ArgumentPosition pos | + Impl::argumentEdge(pred, pos, succ) and + lbl = Label::getLabelFromArgumentPosition(pos) + ) + or + Impl::instanceEdge(pred, succ) and + lbl = Label::instance() + or + Impl::returnEdge(pred, succ) and + lbl = Label::return() + or + exists(EntryPoint entry | + Impl::entryPointEdge(entry, succ) and + pred = root() and + lbl = Label::entryPoint(entry) + ) + } + + /** + * DEPRECATED. Treating the API graph as an explicit labelled graph is deprecated - instead use the methods on `API:Node` directly. + * + * Provides classes modeling the various edges (labels) in the API graph. + */ + deprecated module Label { /** All the possible labels in the API graph. */ - cached - newtype TLabel = + private newtype TLabel = MkLabelMember(string member) { member = any(ConstantReadAccess a).getName() } or MkLabelMethod(string m) { m = any(DataFlow::CallNode c).getMethodName() } or MkLabelReturn() or - MkLabelSubclass() or + MkLabelInstance() or MkLabelKeywordParameter(string name) { any(DataFlowDispatch::ArgumentPosition arg).isKeyword(name) or @@ -923,12 +1401,9 @@ module API { MkLabelBlockParameter() or MkLabelEntryPoint(EntryPoint name) or MkLabelContent(DataFlow::Content content) - } - /** Provides classes modeling the various edges (labels) in the API graph. */ - module Label { /** A label in the API-graph */ - class ApiLabel extends Impl::TLabel { + class ApiLabel extends TLabel { /** Gets a string representation of this label. */ string toString() { result = "???" } } @@ -967,9 +1442,9 @@ module API { override string toString() { result = "getReturn()" } } - /** A label for the subclass relationship. */ - class LabelSubclass extends ApiLabel, MkLabelSubclass { - override string toString() { result = "getASubclass()" } + /** A label for getting instances of a module/class. */ + class LabelInstance extends ApiLabel, MkLabelInstance { + override string toString() { result = "getInstance()" } } /** A label for a keyword parameter. */ @@ -1037,8 +1512,8 @@ module API { /** Gets the `return` edge label. */ LabelReturn return() { any() } - /** Gets the `subclass` edge label. */ - LabelSubclass subclass() { any() } + /** Gets the `instance` edge label. */ + LabelInstance instance() { any() } /** Gets the label representing the given keyword argument/parameter. */ LabelKeywordParameter keywordParameter(string name) { result.getName() = name } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index c6adcbcbe3a..a142fd1f0fb 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -6,12 +6,17 @@ private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.dataflow.SSA private import FlowSummaryImpl as FlowSummaryImpl private import SsaImpl as SsaImpl +private import codeql.ruby.ApiGraphs /** * An element, viewed as a node in a data flow graph. Either an expression * (`ExprNode`) or a parameter (`ParameterNode`). */ class Node extends TNode { + /** Starts backtracking from this node using API graphs. */ + pragma[inline] + API::Node backtrack() { result = API::Internal::getNodeForBacktracking(this) } + /** Gets the expression corresponding to this node, if any. */ CfgNodes::ExprCfgNode asExpr() { result = this.(ExprNode).getExprNode() } @@ -76,6 +81,11 @@ class Node extends TNode { result.asCallableAstNode() = c.asCallable() ) } + + /** Gets the enclosing method, if any. */ + MethodNode getEnclosingMethod() { + result.asCallableAstNode() = this.asExpr().getExpr().getEnclosingMethod() + } } /** A data-flow node corresponding to a call in the control-flow graph. */ @@ -144,6 +154,18 @@ class CallNode extends LocalSourceNode, ExprNode { result.asExpr() = pair.getValue() ) } + + /** + * Gets a potential target of this call, if any. + */ + final CallableNode getATarget() { + result.asCallableAstNode() = this.asExpr().getExpr().(Call).getATarget() + } + + /** + * Holds if this is a `super` call. + */ + final predicate isSuperCall() { this.asExpr().getExpr() instanceof SuperCall } } /** @@ -217,6 +239,10 @@ class SelfParameterNode extends ParameterNode instanceof SelfParameterNodeImpl { class LocalSourceNode extends Node { LocalSourceNode() { isLocalSourceNode(this) } + /** Starts tracking this node forward using API graphs. */ + pragma[inline] + API::Node track() { result = API::Internal::getNodeForForwardTracking(this) } + /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ pragma[inline] predicate flowsTo(Node nodeTo) { hasLocalSource(nodeTo, this) } @@ -359,6 +385,11 @@ private module Cached { ) } + cached + predicate methodHasSuperCall(MethodNode method, CallNode call) { + call.isSuperCall() and method = call.getEnclosingMethod() + } + /** * A place in which a named constant can be looked up during constant lookup. */ @@ -387,6 +418,40 @@ private module Cached { result.asExpr().getExpr() = access } + /** + * Gets a module for which this constant is the reference to an ancestor module. + * + * For example, `M` is the ancestry target of `C` in the following examples: + * ```rb + * class M < C {} + * + * module M + * include C + * end + * + * module M + * prepend C + * end + * ``` + */ + private ModuleNode getAncestryTarget(ConstRef constRef) { result.getAnAncestorExpr() = constRef } + + /** + * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant. + */ + cached + TConstLookupScope getATargetScope(ConstRef constRef) { + forceCachingInSameStage() and + result = MkAncestorLookup(getAncestryTarget(constRef).getAnImmediateDescendent*()) + or + constRef.asConstantAccess() = any(ConstantAccess ac).getScopeExpr() and + result = MkQualifiedLookup(constRef.asConstantAccess()) + or + result = MkNestedLookup(getAncestryTarget(constRef)) + or + result = MkExactLookup(constRef.asConstantAccess().(Namespace).getModule()) + } + cached predicate forceCachingInSameStage() { any() } @@ -1028,6 +1093,33 @@ class ModuleNode instanceof Module { * this predicate. */ ModuleNode getNestedModule(string name) { result = super.getNestedModule(name) } + + /** + * Starts tracking the module object using API graphs. + * + * Concretely, this tracks forward from the following starting points: + * - A constant access that resolves to this module. + * - `self` in the module scope or in a singleton method of the module. + * - A call to `self.class` in an instance method of this module or an ancestor module. + */ + bindingset[this] + pragma[inline] + API::Node trackModule() { result = API::Internal::getModuleNode(this) } + + /** + * Starts tracking instances of this module forward using API graphs. + * + * Concretely, this tracks forward from the following starting points: + * - `self` in instance methods of this module and ancestor modules + * - Calls to `new` on the module object + * + * Note that this includes references to `self` in ancestor modules, but not in descendent modules. + * This is usually the desired behavior, particularly if this module was itself found using + * a call to `getADescendentModule()`. + */ + bindingset[this] + pragma[inline] + API::Node trackInstance() { result = API::Internal::getModuleInstance(this) } } /** @@ -1216,6 +1308,9 @@ class MethodNode extends CallableNode { /** Holds if this method is protected. */ predicate isProtected() { this.asCallableAstNode().isProtected() } + + /** Gets a `super` call in this method. */ + CallNode getASuperCall() { methodHasSuperCall(this, result) } } /** @@ -1291,6 +1386,11 @@ class ArrayLiteralNode extends LocalSourceNode, ExprNode { * Gets an element of the array. */ Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) } + + /** + * Gets the `n`th element of the array. + */ + Node getElement(int n) { result = this.(CallNode).getPositionalArgument(n) } } /** @@ -1331,24 +1431,6 @@ class ConstRef extends LocalSourceNode { not exists(access.getScopeExpr()) } - /** - * Gets a module for which this constant is the reference to an ancestor module. - * - * For example, `M` is the ancestry target of `C` in the following examples: - * ```rb - * class M < C {} - * - * module M - * include C - * end - * - * module M - * prepend C - * end - * ``` - */ - private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this } - /** * Gets the known target module. * @@ -1356,22 +1438,6 @@ class ConstRef extends LocalSourceNode { */ private Module getExactTarget() { result.getAnImmediateReference() = access } - /** - * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant. - */ - cached - private TConstLookupScope getATargetScope() { - forceCachingInSameStage() and - result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*()) - or - access = any(ConstantAccess ac).getScopeExpr() and - result = MkQualifiedLookup(access) - or - result = MkNestedLookup(this.getAncestryTarget()) - or - result = MkExactLookup(access.(Namespace).getModule()) - } - /** * Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes). * @@ -1433,7 +1499,7 @@ class ConstRef extends LocalSourceNode { pragma[inline] ConstRef getConstant(string name) { exists(TConstLookupScope scope | - pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and + pragma[only_bind_into](scope) = getATargetScope(pragma[only_bind_out](this)) and result.accesses(pragma[only_bind_out](scope), name) ) } @@ -1455,7 +1521,14 @@ class ConstRef extends LocalSourceNode { * end * ``` */ - ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() } + pragma[inline] + ModuleNode getADescendentModule() { result = getADescendentModuleInline(this) } +} + +bindingset[ref] +pragma[inline_late] +private ModuleNode getADescendentModuleInline(ConstRef ref) { + MkAncestorLookup(result) = getATargetScope(ref) } /** diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll index 2e3fa21a45b..21bc5f69dcb 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll @@ -44,7 +44,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable { override Call getACall() { exists(API::MethodAccessNode base | ModelOutput::resolvedSummaryBase(type, path, base) and - result = base.getCallNode().asExpr().getExpr() + result = base.asCall().asExpr().getExpr() ) } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll index 4c03522a9c5..ed7a331c452 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -99,9 +99,10 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { // A row of form `any;Method[foo]` should match any method named `foo`. type = "any" and n = 1 and - exists(EntryPointFromAnyType entry | - methodMatchedByName(path, entry.getName()) and - result = entry.getANode() + exists(string methodName, DataFlow::CallNode call | + methodMatchedByName(path, methodName) and + call.getMethodName() = methodName and + result.(API::MethodAccessNode).asCall() = call ) } @@ -112,20 +113,10 @@ API::Node getExtraNodeFromType(string type) { constRef = getConstantFromConstPath(consts) | suffix = "!" and - ( - result.(API::Node::Internal).asSourceInternal() = constRef - or - result.(API::Node::Internal).asSourceInternal() = - constRef.getADescendentModule().getAnOwnModuleSelf() - ) + result = constRef.track() or suffix = "" and - ( - result.(API::Node::Internal).asSourceInternal() = constRef.getAMethodCall("new") - or - result.(API::Node::Internal).asSourceInternal() = - constRef.getADescendentModule().getAnInstanceSelf() - ) + result = constRef.track().getInstance() ) or type = "" and @@ -145,21 +136,6 @@ private predicate methodMatchedByName(AccessPath path, string methodName) { ) } -/** - * An API graph entry point corresponding to a method name such as `foo` in `;any;Method[foo]`. - * - * This ensures that the API graph rooted in that method call is materialized. - */ -private class EntryPointFromAnyType extends API::EntryPoint { - string name; - - EntryPointFromAnyType() { this = "AnyMethod[" + name + "]" and methodMatchedByName(_, name) } - - override DataFlow::CallNode getACall() { result.getMethodName() = name } - - string getName() { result = name } -} - /** * Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`. */ @@ -175,9 +151,11 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getInstance() or token.getName() = "Parameter" and - result = - node.getASuccessor(API::Label::getLabelFromParameterPosition(FlowSummaryImplSpecific::parseArgBody(token - .getAnArgument()))) + exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | + argPos = FlowSummaryImplSpecific::parseParamBody(token.getAnArgument()) and + DataFlowDispatch::parameterMatch(paramPos, argPos) and + result = node.getParameterAtPosition(paramPos) + ) or exists(DataFlow::ContentSet contents | SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and @@ -191,9 +169,11 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { bindingset[token] API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) { token.getName() = "Argument" and - result = - node.getASuccessor(API::Label::getLabelFromArgumentPosition(FlowSummaryImplSpecific::parseParamBody(token - .getAnArgument()))) + exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | + paramPos = FlowSummaryImplSpecific::parseArgBody(token.getAnArgument()) and + DataFlowDispatch::parameterMatch(paramPos, argPos) and + result = node.getArgumentAtPosition(argPos) + ) } /** @@ -211,7 +191,7 @@ predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToke /** An API graph node representing a method call. */ class InvokeNode extends API::MethodAccessNode { /** Gets the number of arguments to the call. */ - int getNumArgument() { result = this.getCallNode().getNumberOfArguments() } + int getNumArgument() { result = this.asCall().getNumberOfArguments() } } /** Gets the `InvokeNode` corresponding to a specific invocation of `node`. */ diff --git a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll new file mode 100644 index 00000000000..e27384b1c7b --- /dev/null +++ b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll @@ -0,0 +1,329 @@ +/** + * Parts of API graphs that can be shared with other dynamic languages. + * + * Depends on TypeTrackerSpecific for the corresponding language. + */ + +private import codeql.Locations +private import codeql.ruby.typetracking.TypeTracker +private import TypeTrackerSpecific + +/** + * The signature to use when instantiating `ApiGraphShared`. + * + * The implementor should define a newtype with at least three branches as follows: + * ```ql + * newtype TApiNode = + * MkForwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + * MkBackwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or + * MkSinkNode(Node node) { ... } or + * ... + * ``` + * + * The three branches should be exposed through `getForwardNode`, `getBackwardNode`, and `getSinkNode`, respectively. + */ +signature module ApiGraphSharedSig { + /** A node in the API graph. */ + class ApiNode { + /** Gets a string representation of this API node. */ + string toString(); + + /** Gets the location associated with this API node, if any. */ + Location getLocation(); + } + + /** + * Gets the forward node with the given type-tracking state. + * + * This node will have outgoing epsilon edges to its type-tracking successors. + */ + ApiNode getForwardNode(TypeTrackingNode node, TypeTracker t); + + /** + * Gets the backward node with the given type-tracking state. + * + * This node will have outgoing epsilon edges to its type-tracking predecessors. + */ + ApiNode getBackwardNode(TypeTrackingNode node, TypeTracker t); + + /** + * Gets the sink node corresponding to `node`. + * + * Since sinks are not generally `LocalSourceNode`s, such nodes are materialised separately in order for + * the API graph to include representatives for sinks. Note that there is no corresponding case for "source" + * nodes as these are represented as forward nodes with initial-state type-trackers. + * + * Sink nodes have outgoing epsilon edges to the backward nodes corresponding to their local sources. + */ + ApiNode getSinkNode(Node node); + + /** + * Holds if a language-specific epsilon edge `pred -> succ` should be generated. + */ + predicate specificEpsilonEdge(ApiNode pred, ApiNode succ); +} + +/** + * Parts of API graphs that can be shared between language implementations. + */ +module ApiGraphShared { + private import S + + /** Gets a local source of `node`. */ + bindingset[node] + pragma[inline_late] + TypeTrackingNode getALocalSourceStrict(Node node) { result = node.getALocalSource() } + + cached + private module Cached { + /** + * Holds if there is an epsilon edge `pred -> succ`. + * + * That relation is reflexive, so `fastTC` produces the equivalent of a reflexive, transitive closure. + */ + pragma[noopt] + cached + predicate epsilonEdge(ApiNode pred, ApiNode succ) { + // forward + exists( + StepSummary summary, TypeTrackingNode predNode, TypeTracker predState, + TypeTrackingNode succNode, TypeTracker succState + | + StepSummary::stepCall(predNode, succNode, summary) + or + StepSummary::stepNoCall(predNode, succNode, summary) + | + pred = getForwardNode(predNode, predState) and + succState = StepSummary::append(predState, summary) and + succ = getForwardNode(succNode, succState) + or + succ = getBackwardNode(predNode, predState) and // swap order for backward flow + succState = StepSummary::append(predState, summary) and + pred = getBackwardNode(succNode, succState) // swap order for backward flow + ) + or + exists(Node sink, TypeTrackingNode localSource | + pred = getSinkNode(sink) and + localSource = getALocalSourceStrict(sink) and + succ = getBackwardStartNode(localSource) + ) + or + specificEpsilonEdge(pred, succ) + or + succ instanceof ApiNode and + succ = pred + } + + /** + * Holds if `pred` can reach `succ` by zero or more epsilon edges. + */ + cached + predicate epsilonStar(ApiNode pred, ApiNode succ) = fastTC(epsilonEdge/2)(pred, succ) + + /** Gets the API node to use when starting forward flow from `source` */ + cached + ApiNode forwardStartNode(TypeTrackingNode source) { + result = getForwardNode(source, TypeTracker::end(false)) + } + + /** Gets the API node to use when starting backward flow from `sink` */ + cached + ApiNode backwardStartNode(TypeTrackingNode sink) { + // There is backward flow A->B iff there is forward flow B->A. + // The starting point of backward flow corresponds to the end of a forward flow, and vice versa. + result = getBackwardNode(sink, TypeTracker::end(_)) + } + + /** Gets `node` as a data flow source. */ + cached + TypeTrackingNode asSourceCached(ApiNode node) { node = forwardEndNode(result) } + + /** Gets `node` as a data flow sink. */ + cached + Node asSinkCached(ApiNode node) { node = getSinkNode(result) } + } + + private import Cached + + /** Gets an API node corresponding to the end of forward-tracking to `localSource`. */ + pragma[nomagic] + private ApiNode forwardEndNode(TypeTrackingNode localSource) { + result = getForwardNode(localSource, TypeTracker::end(_)) + } + + /** Gets an API node corresponding to the end of backtracking to `localSource`. */ + pragma[nomagic] + private ApiNode backwardEndNode(TypeTrackingNode localSource) { + result = getBackwardNode(localSource, TypeTracker::end(false)) + } + + /** Gets a node reachable from `node` by zero or more epsilon edges, including `node` itself. */ + bindingset[node] + pragma[inline_late] + ApiNode getAnEpsilonSuccessorInline(ApiNode node) { epsilonStar(node, result) } + + /** Gets `node` as a data flow sink. */ + bindingset[node] + pragma[inline_late] + Node asSinkInline(ApiNode node) { result = asSinkCached(node) } + + /** Gets `node` as a data flow source. */ + bindingset[node] + pragma[inline_late] + TypeTrackingNode asSourceInline(ApiNode node) { result = asSourceCached(node) } + + /** Gets a value reachable from `source`. */ + bindingset[source] + pragma[inline_late] + Node getAValueReachableFromSourceInline(ApiNode source) { + exists(TypeTrackingNode src | + src = asSourceInline(getAnEpsilonSuccessorInline(source)) and + src.flowsTo(pragma[only_bind_into](result)) + ) + } + + /** Gets a value that can reach `sink`. */ + bindingset[sink] + pragma[inline_late] + Node getAValueReachingSinkInline(ApiNode sink) { + result = asSinkInline(getAnEpsilonSuccessorInline(sink)) + } + + /** + * Gets the starting point for forward-tracking at `node`. + * + * Should be used to obtain the successor of an edge when constructing labelled edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardStartNode(Node node) { result = forwardStartNode(node) } + + /** + * Gets the starting point of backtracking from `node`. + * + * Should be used to obtain the successor of an edge when constructing labelled edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getBackwardStartNode(Node node) { result = backwardStartNode(node) } + + /** + * Gets a possible ending point of forward-tracking at `node`. + * + * Should be used to obtain the predecessor of an edge when constructing labelled edges. + * + * This is not backed by a `cached` predicate, and should only be used for materialising `cached` + * predicates in the API graph implementation - it should not be called in later stages. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardEndNode(Node node) { result = forwardEndNode(node) } + + /** + * Gets a possible ending point backtracking to `node`. + * + * Should be used to obtain the predecessor of an edge when constructing labelled edges. + * + * This is not backed by a `cached` predicate, and should only be used for materialising `cached` + * predicates in the API graph implementation - it should not be called in later stages. + */ + bindingset[node] + pragma[inline_late] + ApiNode getBackwardEndNode(Node node) { result = backwardEndNode(node) } + + /** + * Gets a possible eding point of forward or backward tracking at `node`. + * + * Should be used to obtain the predecessor of an edge generated from store or load edges. + */ + bindingset[node] + pragma[inline_late] + ApiNode getForwardOrBackwardEndNode(Node node) { + result = getForwardEndNode(node) or result = getBackwardEndNode(node) + } + + /** Gets an API node for tracking forward starting at `node`. This is the implementation of `DataFlow::LocalSourceNode.track()` */ + bindingset[node] + pragma[inline_late] + ApiNode getNodeForForwardTracking(Node node) { result = forwardStartNode(node) } + + /** Gets an API node for backtracking starting at `node`. The implementation of `DataFlow::Node.backtrack()`. */ + bindingset[node] + pragma[inline_late] + ApiNode getNodeForBacktracking(Node node) { + result = getBackwardStartNode(getALocalSourceStrict(node)) + } + + /** Parts of the shared module to be re-exported by the user-facing `API` module. */ + module Public { + /** + * The signature to use when instantiating the `ExplainFlow` module. + */ + signature module ExplainFlowSig { + /** Holds if `node` should be a source. */ + predicate isSource(ApiNode node); + + /** Holds if `node` should be a sink. */ + default predicate isSink(ApiNode node) { any() } + + /** Holds if `node` should be skipped in the generated paths. */ + default predicate isHidden(ApiNode node) { none() } + } + + /** + * Module to help debug and visualize the data flows underlying API graphs. + * + * This module exports the query predicates for a path-problem query, and should be imported + * into the top-level of such a query. + * + * The module argument should specify source and sink API nodes, and the resulting query + * will show paths of epsilon edges that go from a source to a sink. Only epsilon edges are visualized. + * + * To condense the output a bit, paths in which the source and sink are the same node are omitted. + */ + module ExplainFlow { + private import T + + private ApiNode relevantNode() { + isSink(result) and + result = getAnEpsilonSuccessorInline(any(ApiNode node | isSource(node))) + or + epsilonEdge(result, relevantNode()) + } + + /** Holds if `node` is part of the graph to visualize. */ + query predicate nodes(ApiNode node) { node = relevantNode() and not isHidden(node) } + + private predicate edgeToHiddenNode(ApiNode pred, ApiNode succ) { + epsilonEdge(pred, succ) and + isHidden(succ) and + pred = relevantNode() and + succ = relevantNode() + } + + /** Holds if `pred -> succ` is an edge in the graph to visualize. */ + query predicate edges(ApiNode pred, ApiNode succ) { + nodes(pred) and + nodes(succ) and + exists(ApiNode mid | + edgeToHiddenNode*(pred, mid) and + epsilonEdge(mid, succ) + ) + } + + /** Holds for each source/sink pair to visualize in the graph. */ + query predicate problems( + ApiNode location, ApiNode sourceNode, ApiNode sinkNode, string message + ) { + nodes(sourceNode) and + nodes(sinkNode) and + isSource(sourceNode) and + isSink(sinkNode) and + sinkNode = getAnEpsilonSuccessorInline(sourceNode) and + sourceNode != sinkNode and + location = sinkNode and + message = "Node flows here" + } + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll index 25521f5f1a5..74c67b5be78 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -55,10 +55,9 @@ private module Cached { ) } - pragma[nomagic] - private TypeTracker noContentTypeTracker(boolean hasCall) { - result = MkTypeTracker(hasCall, noContent()) - } + /** Gets a type tracker with no content and the call bit set to the given value. */ + cached + TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -340,6 +339,8 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { + predicate append = Cached::append/2; + /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -400,6 +401,35 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; + + /** Gets the step summary for a level step. */ + StepSummary levelStep() { result = LevelStep() } + + /** Gets the step summary for a call step. */ + StepSummary callStep() { result = CallStep() } + + /** Gets the step summary for a return step. */ + StepSummary returnStep() { result = ReturnStep() } + + /** Gets the step summary for storing into `content`. */ + StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } + + /** Gets the step summary for loading from `content`. */ + StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } + + /** Gets the step summary for loading from `load` and then storing into `store`. */ + StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { + result = LoadStoreStep(load, store) + } + + /** Gets the step summary for a step that only permits contents matched by `filter`. */ + StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } + + /** Gets the step summary for a step that blocks contents matched by `filter`. */ + StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } + + /** Gets the step summary for a jump step. */ + StepSummary jumpStep() { result = JumpStep() } } /** @@ -545,6 +575,13 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } + + /** + * INTERNAL USE ONLY. + * + * Gets a valid end point of type tracking with the call bit set to the given value. + */ + predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected index 43b6490b052..5677b16fedb 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected @@ -1,8 +1,8 @@ classMethodCalls -| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() | -| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() | +| test1.rb:58:1:58:8 | ForwardNode(call to m) | +| test1.rb:59:1:59:8 | ForwardNode(call to m) | instanceMethodCalls -| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() | -| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() | +| test1.rb:61:1:61:12 | ForwardNode(call to m) | +| test1.rb:62:1:62:12 | ForwardNode(call to m) | flowThroughArray | test1.rb:73:1:73:10 | call to m | diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected similarity index 100% rename from ruby/ql/test/library-tests/dataflow/api-graphs/use.expected rename to ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql new file mode 100644 index 00000000000..93b5aaf745e --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql @@ -0,0 +1,77 @@ +import ruby +import codeql.ruby.ast.internal.TreeSitter +import codeql.ruby.dataflow.internal.AccessPathSyntax +import codeql.ruby.frameworks.data.internal.ApiGraphModels +import codeql.ruby.ApiGraphs +import TestUtilities.InlineExpectationsTest + +class AccessPathFromExpectation extends AccessPath::Range { + AccessPathFromExpectation() { hasExpectationWithValue(_, this) } +} + +API::Node evaluatePath(AccessPath path, int n) { + path instanceof AccessPathFromExpectation and + n = 1 and + exists(AccessPathToken token | token = path.getToken(0) | + token.getName() = "Member" and + result = API::getTopLevelMember(token.getAnArgument()) + or + token.getName() = "Method" and + result = API::getTopLevelCall(token.getAnArgument()) + or + token.getName() = "EntryPoint" and + result = token.getAnArgument().(API::EntryPoint).getANode() + ) + or + result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1)) + or + result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1)) + or + // TODO this is a workaround, support parsing of Method['[]'] instead + path.getToken(n - 1).getName() = "MethodBracket" and + result = evaluatePath(path, n - 1).getMethod("[]") +} + +API::Node evaluatePath(AccessPath path) { result = evaluatePath(path, path.getNumToken()) } + +module ApiUseTest implements TestSig { + string getARelevantTag() { result = ["source", "sink", "call", "reachableFromSource"] } + + predicate hasActualResult(Location location, string element, string tag, string value) { + // All results are considered optional + none() + } + + predicate hasOptionalResult(Location location, string element, string tag, string value) { + exists(API::Node apiNode, DataFlow::Node dataflowNode | + apiNode = evaluatePath(value) and + ( + tag = "source" and dataflowNode = apiNode.asSource() + or + tag = "reachableFromSource" and dataflowNode = apiNode.getAValueReachableFromSource() + or + tag = "sink" and dataflowNode = apiNode.asSink() + or + tag = "call" and dataflowNode = apiNode.asCall() + ) and + location = dataflowNode.getLocation() and + element = dataflowNode.toString() + ) + } +} + +import MakeTest + +class CustomEntryPointCall extends API::EntryPoint { + CustomEntryPointCall() { this = "CustomEntryPointCall" } + + override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } +} + +class CustomEntryPointUse extends API::EntryPoint { + CustomEntryPointUse() { this = "CustomEntryPointUse" } + + override DataFlow::LocalSourceNode getASource() { + result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" + } +} diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb index 34c4d17d212..35cf4471b48 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb @@ -1,39 +1,39 @@ -Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn() - a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn() - b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn() -end #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn() +Something.foo.withCallback do |a, b| #$ source=Member[Something].Method[foo].ReturnValue + a.something #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[0].Method[something].ReturnValue + b.somethingElse #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[1].Method[somethingElse].ReturnValue +end #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].ReturnValue -Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn() - b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn() -end #$ use=getMember("Something").getMethod("withNamedArg").getReturn() +Something.withNamedArg do |a:, b: nil| #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[a:].Method[something].ReturnValue + b.somethingElse #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[b:].Method[somethingElse].ReturnValue +end #$ source=Member[Something].Method[withNamedArg].ReturnValue -Something.withLambda ->(a, b) { #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn() - b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn() -} #$ use=getMember("Something").getMethod("withLambda").getReturn() +Something.withLambda ->(a, b) { #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[0].Method[something].ReturnValue + b.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[1].Method[something].ReturnValue +} #$ source=Member[Something].Method[withLambda].ReturnValue -Something.namedCallback( #$ use=getMember("Something") +Something.namedCallback( #$ source=Member[Something] onEvent: ->(a, b) { - a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn() - b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn() + a.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[0].Method[something].ReturnValue + b.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[1].Method[something].ReturnValue } -) #$ use=getMember("Something").getMethod("namedCallback").getReturn() +) #$ source=Member[Something].Method[namedCallback].ReturnValue -Something.nestedCall1 do |a| #$ use=getMember("Something") - a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0) - b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn() - end #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn() -end #$ use=getMember("Something").getMethod("nestedCall1").getReturn() +Something.nestedCall1 do |a| #$ source=Member[Something] + a.nestedCall2 do |b:| #$ reachableFromSource=Member[Something].Method[nestedCall1].Argument[block].Parameter[0] + b.something #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].Argument[block].Parameter[b:].Method[something].ReturnValue + end #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].ReturnValue +end #$ source=Member[Something].Method[nestedCall1].ReturnValue def getCallback() ->(x) { - x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn() + x.something #$ source=Member[Something].Method[indirectCallback].Argument[0].Parameter[0].Method[something].ReturnValue } end -Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn() +Something.indirectCallback(getCallback()) #$ source=Member[Something].Method[indirectCallback].ReturnValue -Something.withMixed do |a, *args, b| #$ use=getMember("Something") - a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn() +Something.withMixed do |a, *args, b| #$ source=Member[Something] + a.something #$ source=Member[Something].Method[withMixed].Argument[block].Parameter[0].Method[something].ReturnValue # b.something # not currently handled correctly -end #$ use=getMember("Something").getMethod("withMixed").getReturn() +end #$ source=Member[Something].Method[withMixed].ReturnValue diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb new file mode 100644 index 00000000000..b0e4f07e701 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb @@ -0,0 +1,31 @@ +def chained_access1 + Something.foo [[[ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ]]] +end + +def chained_access2 + array = [] + array[0] = [[ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ]] + Something.foo array +end + +def chained_access3 + array = [[]] + array[0][0] = [ + 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] + ] + Something.foo array +end + +def chained_access4 + Something.foo { + :one => { + :two => { + :three => 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[:one].Element[:two].Element[:three] + } + } + } +end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb new file mode 100644 index 00000000000..63a4b4c3dce --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb @@ -0,0 +1,64 @@ +class BaseClass + def inheritedInstanceMethod + yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[inheritedInstanceMethod].Parameter[block].Argument[0] + end + + def self.inheritedSingletonMethod + yield "taint" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[inheritedSingletonMethod].Parameter[block].Argument[0] + end +end + +class ClassWithCallbacks < BaseClass + def instanceMethod + yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[instanceMethod].Parameter[block].Argument[0] + end + + def self.singletonMethod + yield "bar" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[singletonMethod].Parameter[block].Argument[0] + end + + def escapeSelf + Something.baz { self } + end + + def self.escapeSingletonSelf + Something.baz { self } + end + + def self.foo x + x # $ reachableFromSource=Member[BaseClass].Method[foo].Parameter[0] + x # $ reachableFromSource=Member[ClassWithCallbacks].Method[foo].Parameter[0] + x # $ reachableFromSource=Member[Subclass].Method[foo].Parameter[0] + end + + def bar x + x # $ reachableFromSource=Member[BaseClass].Instance.Method[bar].Parameter[0] + x # $ reachableFromSource=Member[ClassWithCallbacks].Instance.Method[bar].Parameter[0] + x # $ reachableFromSource=Member[Subclass].Instance.Method[bar].Parameter[0] + end +end + +class Subclass < ClassWithCallbacks + def instanceMethodInSubclass + yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[instanceMethodInSubclass].Parameter[block].Argument[0] + end + + def self.singletonMethodInSubclass + yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[singletonMethodInSubclass].Parameter[block].Argument[0] + end +end + +Something.foo { ClassWithCallbacks.new } +Something.bar { ClassWithCallbacks } + +class ClassWithCallMethod + def call x + x # $ reachableFromSource=Method[topLevelMethod].Argument[0].Parameter[0] + "bar" # $ sink=Method[topLevelMethod].Argument[0].ReturnValue + end +end + +topLevelMethod ClassWithCallMethod.new + +blah = topLevelMethod +blah # $ reachableFromSource=Method[topLevelMethod].ReturnValue diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb index 86b8bce9587..3af793dd4f7 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb @@ -1,34 +1,34 @@ -MyModule #$ use=getMember("MyModule") -print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn() -Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0) -Object::Kernel #$ use=getMember("Kernel") -Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() +MyModule #$ source=Member[MyModule] +print MyModule.foo #$ source=Member[MyModule].Method[foo].ReturnValue +Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue sink=Member[Kernel].Method[print].Argument[0] +Object::Kernel #$ source=Member[Kernel] +Object::Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue begin - print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn() - raise AttributeError #$ use=getMember("AttributeError") -rescue AttributeError => e #$ use=getMember("AttributeError") - Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() + print MyModule.bar #$ source=Member[MyModule].Method[bar].ReturnValue + raise AttributeError #$ source=Member[AttributeError] +rescue AttributeError => e #$ source=Member[AttributeError] + Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue end -Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn() -Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +Unknown.new.run #$ source=Member[Unknown].Method[new].ReturnValue.Method[run].ReturnValue +Foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] -Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn() -Const.each do |c| #$ use=getMember("Const") - puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element) -end #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock() +Const = [1, 2, 3] #$ source=Member[Array].MethodBracket.ReturnValue +Const.each do |c| #$ source=Member[Const] + puts c #$ reachableFromSource=Member[Const].Method[each].Argument[block].Parameter[0] reachableFromSource=Member[Const].Element[any] +end #$ source=Member[Const].Method[each].ReturnValue sink=Member[Const].Method[each].Argument[block] -foo = Foo #$ use=getMember("Foo") -foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +foo = Foo #$ source=Member[Foo] +foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] -FooAlias = Foo #$ use=getMember("Foo") -FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") +FooAlias = Foo #$ source=Member[Foo] +FooAlias::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] source=Member[FooAlias].Member[Bar].Member[Baz] module Outer module Inner end end -Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn() +Outer::Inner.foo #$ source=Member[Outer].Member[Inner].Method[foo].ReturnValue module M1 class C1 @@ -40,36 +40,36 @@ module M1 end end -class C2 < M1::C1 #$ use=getMember("M1").getMember("C1") +class C2 < M1::C1 #$ source=Member[M1].Member[C1] end module M2 - class C3 < M1::C1 #$ use=getMember("M1").getMember("C1") + class C3 < M1::C1 #$ source=Member[M1].Member[C1] end - class C4 < C2 #$ use=getMember("C2") + class C4 < C2 #$ source=Member[C2] end end -C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass() -M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass() -M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass() +C2 #$ source=Member[C2] reachableFromSource=Member[M1].Member[C1] +M2::C3 #$ source=Member[M2].Member[C3] reachableFromSource=Member[M1].Member[C1] +M2::C4 #$ source=Member[M2].Member[C4] reachableFromSource=Member[C2] reachableFromSource=Member[M1].Member[C1] -M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn() -M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn() +M1::C1.m #$ source=Member[M1].Member[C1].Method[m].ReturnValue +M2::C3.m #$ source=Member[M2].Member[C3].Method[m].ReturnValue source=Member[M1].Member[C1].Method[m].ReturnValue -M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() -M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() +M1::C1.new.m #$ source=Member[M1].Member[C1].Method[new].ReturnValue.Method[m].ReturnValue +M2::C3.new.m #$ source=Member[M2].Member[C3].Method[new].ReturnValue.Method[m].ReturnValue -Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b") +Foo.foo(a,b:c) #$ source=Member[Foo].Method[foo].ReturnValue sink=Member[Foo].Method[foo].Argument[0] sink=Member[Foo].Method[foo].Argument[b:] def userDefinedFunction(x, y) x.noApiGraph(y) - x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0) - x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse") + x.customEntryPointCall(y) #$ call=EntryPoint[CustomEntryPointCall] source=EntryPoint[CustomEntryPointCall].ReturnValue sink=EntryPoint[CustomEntryPointCall].Parameter[0] + x.customEntryPointUse(y) #$ source=EntryPoint[CustomEntryPointUse] end -array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn() -array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn() +array = [A::B::C] #$ source=Member[Array].MethodBracket.ReturnValue +array[0].m #$ source=Member[A].Member[B].Member[C].Method[m].ReturnValue source=Member[Array].MethodBracket.ReturnValue.Element[0].Method[m].ReturnValue -A::B::C[0] #$ use=getMember("A").getMember("B").getMember("C").getContent(element_0) +A::B::C[0] #$ source=Member[A].Member[B].Member[C].Element[0] diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql deleted file mode 100644 index a0c11640ce0..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql +++ /dev/null @@ -1,88 +0,0 @@ -import codeql.ruby.AST -import codeql.ruby.DataFlow -import TestUtilities.InlineExpectationsTest -import codeql.ruby.ApiGraphs - -class CustomEntryPointCall extends API::EntryPoint { - CustomEntryPointCall() { this = "CustomEntryPointCall" } - - override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } -} - -class CustomEntryPointUse extends API::EntryPoint { - CustomEntryPointUse() { this = "CustomEntryPointUse" } - - override DataFlow::LocalSourceNode getASource() { - result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" - } -} - -module ApiUseTest implements TestSig { - string getARelevantTag() { result = ["use", "def", "call"] } - - private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) { - l = n.getLocation() and - ( - tag = "use" and - n = a.getAValueReachableFromSource() - or - tag = "def" and - n = a.asSink() - or - tag = "call" and - n = a.(API::MethodAccessNode).getCallNode() - ) - } - - predicate hasActualResult(Location location, string element, string tag, string value) { - tag = "use" and // def tags are always optional - exists(DataFlow::Node n | relevantNode(_, n, location, tag) | - // Only report the longest path on this line: - value = - max(API::Node a2, Location l2, DataFlow::Node n2 | - relevantNode(a2, n2, l2, tag) and - l2.getFile() = location.getFile() and - l2.getEndLine() = location.getEndLine() - | - a2.getPath() - order by - size(n2.asExpr().getExpr()), a2.getPath().length() desc, a2.getPath() desc - ) and - element = n.toString() - ) - } - - // We also permit optional annotations for any other path on the line. - // This is used to test subclass paths, which typically have a shorter canonical path. - predicate hasOptionalResult(Location location, string element, string tag, string value) { - exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) | - element = n.toString() and - value = getAPath(a, _) - ) - } -} - -import MakeTest - -private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) } - -/** - * Gets a path of the given `length` from the root to the given node. - * This is a copy of `API::getAPath()` without the restriction on path length, - * which would otherwise rule out paths involving `getASubclass()`. - */ -string getAPath(API::Node node, int length) { - node instanceof API::Root and - length = 0 and - result = "" - or - exists(API::Node pred, API::Label::ApiLabel lbl, string predpath | - pred.getASuccessor(lbl) = node and - predpath = getAPath(pred, length - 1) and - exists(string dot | if length = 1 then dot = "" else dot = "." | - result = predpath + dot + lbl and - // avoid producing strings longer than 1MB - result.length() < 1000 * 1000 - ) - ) -} diff --git a/shared/util/codeql/util/test/InlineExpectationsTest.qll b/shared/util/codeql/util/test/InlineExpectationsTest.qll index 7d3806cb4b5..8dc39af47ce 100644 --- a/shared/util/codeql/util/test/InlineExpectationsTest.qll +++ b/shared/util/codeql/util/test/InlineExpectationsTest.qll @@ -444,6 +444,19 @@ module Make { class FalseNegativeExpectation = LegacyTest::FalseNegativeTestExpectation; class InvalidExpectation = LegacyTest::InvalidTestExpectation; + + /** + * Holds if the expectation `tag=value` is found in one or more expectation comments. + * + * This can be used when writing tests where the set of possible values must be known in advance, + * for example, when testing a predicate for which `value` is part of the binding set. + */ + predicate hasExpectationWithValue(string tag, string value) { + exists(string tags | + getAnExpectation(_, _, _, tags, value) and + tag = tags.splitAt(",") + ) + } } /** From 5b05e72d27033005e49efcfd2a7db5be611aa084 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:02:39 +0200 Subject: [PATCH 06/93] Ruby: switch to local dataflow when dealing with Kernel/IO --- ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll | 3 ++- ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll index b445521adb8..ad87ee37ecd 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll @@ -19,7 +19,8 @@ module Kernel { */ class KernelMethodCall extends DataFlow::CallNode { KernelMethodCall() { - this = API::getTopLevelMember("Kernel").getAMethodCall(_) + // Match Kernel calls using local flow, to avoid finding singleton calls on subclasses + this = DataFlow::getConstant("Kernel").getAMethodCall(_) or this.asExpr().getExpr() instanceof UnknownMethodCall and ( diff --git a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll index 31c9055d7cc..3653fb23cee 100644 --- a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll @@ -55,7 +55,8 @@ class AmbiguousPathCall extends DataFlow::CallNode { } private predicate methodCallOnlyOnIO(DataFlow::CallNode node, string methodName) { - node = API::getTopLevelMember("IO").getAMethodCall(methodName) and + // Use local flow to find calls to 'IO' without subclasses + node = DataFlow::getConstant("IO").getAMethodCall(methodName) and not node = API::getTopLevelMember("File").getAMethodCall(methodName) // needed in e.g. opal/opal, where some calls have both paths (opal implements an own corelib) } From 61cda97163fe43abfe2886444836e823547ff6d8 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:03:25 +0200 Subject: [PATCH 07/93] Ruby: rename some call sites --- .../ruby/experimental/UnicodeBypassValidationQuery.qll | 8 ++++---- .../codeql/ruby/experimental/ZipSlipCustomizations.qll | 2 +- ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll | 4 ++-- ruby/ql/lib/codeql/ruby/security/OpenSSL.qll | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll index 5c24978c4c3..449e05ad9e8 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll @@ -91,19 +91,19 @@ class Configuration extends TaintTracking::Configuration { // unicode_utils exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UnicodeUtils").getMethod(["nfkd", "nfc", "nfd", "nfkc"]) and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // eprun exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("Eprun").getMethod("normalize") and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // unf exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UNF").getMember("Normalizer").getMethod("normalize") and - sink = mac.getParameter(0).asSink() + sink = mac.getArgument(0).asSink() ) or // ActiveSupport::Multibyte::Chars @@ -113,7 +113,7 @@ class Configuration extends TaintTracking::Configuration { .getMember("Multibyte") .getMember("Chars") .getMethod("new") - .getCallNode() and + .asCall() and n = cn.getAMethodCall("normalize") and sink = cn.getArgument(0) ) diff --git a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll index e94cabb414c..656ceedbe2b 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll @@ -89,7 +89,7 @@ module ZipSlip { // If argument refers to a string object, then it's a hardcoded path and // this file is safe. not zipOpen - .getCallNode() + .asCall() .getArgument(0) .getALocalSource() .getConstantValue() diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll index 4095beb10af..f7345b22ed1 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll @@ -24,7 +24,7 @@ module Gem { GemSpec() { this.getExtension() = "gemspec" and - specCall = API::root().getMember("Gem").getMember("Specification").getMethod("new") and + specCall = API::getTopLevelMember("Gem").getMember("Specification").getMethod("new") and specCall.getLocation().getFile() = this } @@ -42,7 +42,7 @@ module Gem { .getBlock() .getParameter(0) .getMethod(name + "=") - .getParameter(0) + .getArgument(0) .asSink() .asExpr() .getExpr() diff --git a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll index 261a7af462f..637f7dab04a 100644 --- a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll +++ b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll @@ -597,7 +597,7 @@ private module Digest { call = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("new") | this = call.getReturn().getAMethodCall(["digest", "update", "<<"]) and - algo.matchesName(call.getCallNode() + algo.matchesName(call.asCall() .getArgument(0) .asExpr() .getExpr() @@ -619,7 +619,7 @@ private module Digest { Cryptography::HashingAlgorithm algo; DigestCallDirect() { - this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").getCallNode() and + this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").asCall() and algo.matchesName(this.getArgument(0).asExpr().getExpr().getConstantValue().getString()) } From 2ef010f1c0490c9b40b63c80767a6759e4e7d410 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:04:00 +0200 Subject: [PATCH 08/93] Ruby: update GraphQL model --- .../ql/lib/codeql/ruby/frameworks/GraphQL.qll | 25 +++---------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 8e673c4255d..98fbe241404 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -39,13 +39,8 @@ private API::Node graphQlSchema() { result = API::getTopLevelMember("GraphQL").g */ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { GraphqlRelayClassicMutationClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("RelayClassicMutation") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = + graphQlSchema().getMember("RelayClassicMutation").getADescendentModule().getADeclaration() } } @@ -74,13 +69,7 @@ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { */ private class GraphqlSchemaResolverClass extends ClassDeclaration { GraphqlSchemaResolverClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("Resolver") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = graphQlSchema().getMember("Resolver").getADescendentModule().getADeclaration() } } @@ -103,13 +92,7 @@ private string getASupportedHttpMethod() { result = ["get", "post"] } */ class GraphqlSchemaObjectClass extends ClassDeclaration { GraphqlSchemaObjectClass() { - this.getSuperclassExpr() = - graphQlSchema() - .getMember("Object") - .getASubclass() - .getAValueReachableFromSource() - .asExpr() - .getExpr() + this = graphQlSchema().getMember("Object").getADescendentModule().getADeclaration() } /** Gets a `GraphqlFieldDefinitionMethodCall` called in this class. */ From b305c13b65e7af8b81e8c8485f3b8c55faa80d64 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:04:12 +0200 Subject: [PATCH 09/93] Ruby: update SQLite3 model --- .../ql/lib/codeql/ruby/frameworks/Sqlite3.qll | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index 70744d6fcc8..208bff8d8c9 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll @@ -15,21 +15,19 @@ private import codeql.ruby.Concepts * https://github.com/sparklemotion/sqlite3-ruby */ module Sqlite3 { + private API::Node databaseConst() { + result = API::getTopLevelMember("SQLite3").getMember("Database") + } + + private API::Node dbInstance() { + result = databaseConst().getInstance() + or + result = databaseConst().getMethod("new").getBlock().getParameter(0) + } + /** Gets a method call with a receiver that is a database instance. */ private DataFlow::CallNode getADatabaseMethodCall(string methodName) { - exists(API::Node dbInstance | - dbInstance = API::getTopLevelMember("SQLite3").getMember("Database").getInstance() and - ( - result = dbInstance.getAMethodCall(methodName) - or - // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } - exists(DataFlow::BlockNode block | - result.getMethodName() = methodName and - block = dbInstance.getAValueReachableFromSource().(DataFlow::CallNode).getBlock() and - block.getParameter(0).flowsTo(result.getReceiver()) - ) - ) - ) + result = dbInstance().getAMethodCall(methodName) } /** A prepared but unexecuted SQL statement. */ From f8ae5301a4054257213c6a922a23583f67f5da63 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:04:53 +0200 Subject: [PATCH 10/93] Ruby: update Twirp This used right-to-left evaluation for API graphs, which is not supported anymore --- ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll | 29 ++------------------ 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll index 73ef87f5fd5..a315ae100ce 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll @@ -21,38 +21,15 @@ module Twirp { this = API::getTopLevelMember("Twirp").getMember("Service").getAnInstantiation() } - /** - * Gets a local source node for the Service instantiation argument (the service handler). - */ - private DataFlow::LocalSourceNode getHandlerSource() { - result = this.getArgument(0).getALocalSource() - } - - /** - * Gets the API::Node for the service handler's class. - */ - private API::Node getAHandlerClassApiNode() { - result.getAnInstantiation() = this.getHandlerSource() - } - - /** - * Gets the AST module for the service handler's class. - */ - private Ast::Module getAHandlerClassAstNode() { - result = - this.getAHandlerClassApiNode() - .asSource() - .asExpr() - .(CfgNodes::ExprNodes::ConstantReadAccessCfgNode) - .getExpr() - .getModule() + private DataFlow::ClassNode getAHandlerClass() { + result.getAnImmediateReference().getAMethodCall("new").flowsTo(this.getArgument(0)) } /** * Gets a handler's method. */ Ast::Method getAHandlerMethod() { - result = this.getAHandlerClassAstNode().getAnInstanceMethod() + result = this.getAHandlerClass().getAnAncestor().getAnOwnInstanceMethod().asCallableAstNode() } } From 1ae41484da02a819a486c05a8428229d7e4f05f0 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:05:15 +0200 Subject: [PATCH 11/93] Ruby: Use new features in ActionMailbox model --- ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll index 13607f67926..f237e42a9a9 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll @@ -27,8 +27,8 @@ module ActionMailbox { Mail() { this = [ - controller().getAnInstanceSelf().getAMethodCall("inbound_email").getAMethodCall("mail"), - controller().getAnInstanceSelf().getAMethodCall("mail") + controller().trackInstance().getReturn("inbound_email").getAMethodCall("mail"), + controller().trackInstance().getAMethodCall("mail") ] } } @@ -40,7 +40,7 @@ module ActionMailbox { RemoteContent() { this = any(Mail m) - .(DataFlow::LocalSourceNode) + .track() .getAMethodCall([ "body", "to", "from", "raw_source", "subject", "from_address", "recipients_addresses", "cc_addresses", "bcc_addresses", "in_reply_to", From fbfa31937f4b479aaf99d6c369260fa483f3aa0c Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:05:57 +0200 Subject: [PATCH 12/93] Ruby: use new features in ActionMailer --- .../codeql/ruby/frameworks/ActionMailer.qll | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll index af183333d3d..4884f46aac3 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll @@ -4,12 +4,26 @@ private import codeql.ruby.AST private import codeql.ruby.ApiGraphs +private import codeql.ruby.DataFlow private import codeql.ruby.frameworks.internal.Rails /** * Provides modeling for the `ActionMailer` library. */ module ActionMailer { + private DataFlow::ClassNode actionMailerClass() { + result = + [ + DataFlow::getConstant("ActionMailer").getConstant("Base"), + // In Rails applications `ApplicationMailer` typically extends + // `ActionMailer::Base`, but we treat it separately in case the + // `ApplicationMailer` definition is not in the database. + DataFlow::getConstant("ApplicationMailer") + ].getADescendentModule() + } + + private API::Node actionMailerInstance() { result = actionMailerClass().trackInstance() } + /** * A `ClassDeclaration` for a class that extends `ActionMailer::Base`. * For example, @@ -21,33 +35,11 @@ module ActionMailer { * ``` */ class MailerClass extends ClassDeclaration { - MailerClass() { - this.getSuperclassExpr() = - [ - API::getTopLevelMember("ActionMailer").getMember("Base"), - // In Rails applications `ApplicationMailer` typically extends - // `ActionMailer::Base`, but we treat it separately in case the - // `ApplicationMailer` definition is not in the database. - API::getTopLevelMember("ApplicationMailer") - ].getASubclass().getAValueReachableFromSource().asExpr().getExpr() - } - } - - /** A method call with a `self` receiver from within a mailer class */ - private class ContextCall extends MethodCall { - private MailerClass mailerClass; - - ContextCall() { - this.getReceiver() instanceof SelfVariableAccess and - this.getEnclosingModule() = mailerClass - } - - /** Gets the mailer class containing this method. */ - MailerClass getMailerClass() { result = mailerClass } + MailerClass() { this = actionMailerClass().getADeclaration() } } /** A call to `params` from within a mailer. */ - class ParamsCall extends ContextCall, ParamsCallImpl { - ParamsCall() { this.getMethodName() = "params" } + class ParamsCall extends ParamsCallImpl { + ParamsCall() { this = actionMailerInstance().getAMethodCall("params").asExpr().getExpr() } } } From bb3b973b32aba9ab7a92f29d2781625be8d5a9e5 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:06:35 +0200 Subject: [PATCH 13/93] Ruby: use new features in ActionController --- .../ruby/frameworks/ActionController.qll | 47 ++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index 31bdc42e350..a687837f8fd 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -83,8 +83,8 @@ class ActionControllerClass extends DataFlow::ClassNode { } } -private DataFlow::LocalSourceNode actionControllerInstance() { - result = any(ActionControllerClass cls).getSelf() +private API::Node actionControllerInstance() { + result = any(ActionControllerClass cls).getSelf().track() } /** @@ -222,19 +222,19 @@ private class ActionControllerRenderToCall extends RenderToCallImpl { } } +pragma[nomagic] +private DataFlow::CallNode renderCall() { + // ActionController#render is an alias for ActionController::Renderer#render + result = + [ + any(ActionControllerClass c).trackModule().getAMethodCall("render"), + any(ActionControllerClass c).trackModule().getReturn("renderer").getAMethodCall("render") + ] +} + /** A call to `ActionController::Renderer#render`. */ private class RendererRenderCall extends RenderCallImpl { - RendererRenderCall() { - this = - [ - // ActionController#render is an alias for ActionController::Renderer#render - any(ActionControllerClass c).getAnImmediateReference().getAMethodCall("render"), - any(ActionControllerClass c) - .getAnImmediateReference() - .getAMethodCall("renderer") - .getAMethodCall("render") - ].asExpr().getExpr() - } + RendererRenderCall() { this = renderCall().asExpr().getExpr() } } /** A call to `html_escape` from within a controller. */ @@ -260,6 +260,7 @@ class RedirectToCall extends MethodCall { this = controller .getSelf() + .track() .getAMethodCall(["redirect_to", "redirect_back", "redirect_back_or_to"]) .asExpr() .getExpr() @@ -600,9 +601,7 @@ private module ParamsSummaries { * response. */ private module Response { - DataFlow::LocalSourceNode response() { - result = actionControllerInstance().getAMethodCall("response") - } + API::Node response() { result = actionControllerInstance().getReturn("response") } class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range { BodyWrite() { this = response().getAMethodCall("body=") } @@ -628,7 +627,7 @@ private module Response { HeaderWrite() { // response.header[key] = val // response.headers[key] = val - this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=") + this = response().getReturn(["header", "headers"]).getAMethodCall("[]=") or // response.set_header(key) = val // response[header] = val @@ -673,18 +672,12 @@ private module Response { } } -private class ActionControllerLoggerInstance extends DataFlow::Node { - ActionControllerLoggerInstance() { - this = actionControllerInstance().getAMethodCall("logger") - or - any(ActionControllerLoggerInstance i).(DataFlow::LocalSourceNode).flowsTo(this) - } -} - private class ActionControllerLoggingCall extends DataFlow::CallNode, Logging::Range { ActionControllerLoggingCall() { - this.getReceiver() instanceof ActionControllerLoggerInstance and - this.getMethodName() = ["debug", "error", "fatal", "info", "unknown", "warn"] + this = + actionControllerInstance() + .getReturn("logger") + .getAMethodCall(["debug", "error", "fatal", "info", "unknown", "warn"]) } // Note: this is identical to the definition `stdlib.Logger.LoggerInfoStyleCall`. From 8bc4193ce0b61167a067cea6127453212f9a86e4 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:07:21 +0200 Subject: [PATCH 14/93] Ruby: minor overhaul of ActiveRecord model Old version had scalability issues when adding taking more interprocedural flow and inheritance into account. --- .../codeql/ruby/frameworks/ActiveRecord.qll | 265 ++++++++++-------- .../active_record/ActiveRecord.expected | 95 ++++++- .../frameworks/active_record/ActiveRecord.ql | 14 +- 3 files changed, 247 insertions(+), 127 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index fcca078f933..f9c15a4c211 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -30,10 +30,8 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName) methodName = objectInstanceMethodName() } -private API::Node activeRecordClassApiNode() { +private API::Node activeRecordBaseClass() { result = - // class Foo < ActiveRecord::Base - // class Bar < Foo [ API::getTopLevelMember("ActiveRecord").getMember("Base"), // In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we @@ -42,6 +40,46 @@ private API::Node activeRecordClassApiNode() { ] } +/** + * Gets an object with methods from the ActiveRecord query interface. + */ +private API::Node activeRecordQueryBuilder() { + result = activeRecordBaseClass() + or + result = activeRecordBaseClass().getInstance() + or + // Assume any method call might return an ActiveRecord::Relation + // These are dynamically generated + result = activeRecordQueryBuilderMethodAccess(_).getReturn() +} + +/** Gets a call targeting the ActiveRecord query interface. */ +private API::MethodAccessNode activeRecordQueryBuilderMethodAccess(string name) { + result = activeRecordQueryBuilder().getMethod(name) and + // Due to the heuristic tracking of query builder objects, add a restriction for methods with a known call target + not isUnlikelyExternalCall(result) +} + +/** Gets a call targeting the ActiveRecord query interface. */ +private DataFlow::CallNode activeRecordQueryBuilderCall(string name) { + result = activeRecordQueryBuilderMethodAccess(name).asCall() +} + +/** + * Holds if `call` is unlikely to call into an external library, since it has a possible + * call target in its enclosing module. + */ +private predicate isUnlikelyExternalCall(API::MethodAccessNode node) { + exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | call = node.asCall() | + call.getATarget() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] and + call.getEnclosingMethod() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] + ) +} + +private API::Node activeRecordConnectionInstance() { + result = activeRecordBaseClass().getReturn("connection") +} + /** * A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example, * @@ -55,20 +93,19 @@ private API::Node activeRecordClassApiNode() { * ``` */ class ActiveRecordModelClass extends ClassDeclaration { + private DataFlow::ClassNode cls; + ActiveRecordModelClass() { - this.getSuperclassExpr() = - activeRecordClassApiNode().getASubclass().getAValueReachableFromSource().asExpr().getExpr() + cls = activeRecordBaseClass().getADescendentModule() and this = cls.getADeclaration() } // Gets the class declaration for this class and all of its super classes - private ModuleBase getAllClassDeclarations() { - result = this.getModule().getSuperClass*().getADeclaration() - } + private ModuleBase getAllClassDeclarations() { result = cls.getAnAncestor().getADeclaration() } /** * Gets methods defined in this class that may access a field from the database. */ - Method getAPotentialFieldAccessMethod() { + deprecated Method getAPotentialFieldAccessMethod() { // It's a method on this class or one of its super classes result = this.getAllClassDeclarations().getAMethod() and // There is a value that can be returned by this method which may include field data @@ -90,58 +127,84 @@ class ActiveRecordModelClass extends ClassDeclaration { ) ) } + + /** Gets the class as a `DataFlow::ClasNode`. */ + DataFlow::ClassNode getClassNode() { result = cls } } -/** A class method call whose receiver is an `ActiveRecordModelClass`. */ -class ActiveRecordModelClassMethodCall extends MethodCall { - private ActiveRecordModelClass recvCls; +/** + * Gets a potential reference to an ActiveRecord class object. + */ +deprecated private API::Node getAnActiveRecordModelClassRef() { + result = any(ActiveRecordModelClass cls).getClassNode().trackModule() + or + // For methods with an unknown call target, assume this might be a database field, thus returning another ActiveRecord object. + // In this case we do not know which class it belongs to, which is why this predicate can't associate the reference with a specific class. + result = getAnUnknownActiveRecordModelClassCall().getReturn() +} +/** + * Gets a call performed on an ActiveRecord class object, without a known call target in the codebase. + */ +deprecated private API::MethodAccessNode getAnUnknownActiveRecordModelClassCall() { + result = getAnActiveRecordModelClassRef().getMethod(_) and + result.asCall().asExpr().getExpr() instanceof UnknownMethodCall +} + +/** + * DEPRECATED. Use `ActiveRecordModelClass.getClassNode().trackModule().getMethod()` instead. + * + * A class method call whose receiver is an `ActiveRecordModelClass`. + */ +deprecated class ActiveRecordModelClassMethodCall extends MethodCall { ActiveRecordModelClassMethodCall() { - // e.g. Foo.where(...) - recvCls.getModule() = this.getReceiver().(ConstantReadAccess).getModule() - or - // e.g. Foo.joins(:bars).where(...) - recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass() - or - // e.g. self.where(...) within an ActiveRecordModelClass - this.getReceiver() instanceof SelfVariableAccess and - this.getEnclosingModule() = recvCls + this = getAnUnknownActiveRecordModelClassCall().asCall().asExpr().getExpr() } - /** The `ActiveRecordModelClass` of the receiver of this method. */ - ActiveRecordModelClass getReceiverClass() { result = recvCls } + /** Gets the `ActiveRecordModelClass` of the receiver of this method, if it can be determined. */ + ActiveRecordModelClass getReceiverClass() { + this = result.getClassNode().trackModule().getMethod(_).asCall().asExpr().getExpr() + } } -private Expr sqlFragmentArgument(MethodCall call) { - exists(string methodName | - methodName = call.getMethodName() and - ( - methodName = - [ - "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", - "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", - "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", - "rewhere", "select", "reselect", "update_all" - ] and - result = call.getArgument(0) - or - methodName = "calculate" and result = call.getArgument(1) - or - methodName in ["average", "count", "maximum", "minimum", "sum", "count_by_sql"] and - result = call.getArgument(0) - or - // This format was supported until Rails 2.3.8 - methodName = ["all", "find", "first", "last"] and - result = call.getKeywordArgument("conditions") - or - methodName = "reload" and - result = call.getKeywordArgument("lock") - or - // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to - // SQLi if user supplied input is passed in as an argument. - methodName = "annotate" and - result = call.getArgument(_) - ) +private predicate sqlFragmentArgumentInner(DataFlow::CallNode call, DataFlow::Node sink) { + call = + activeRecordQueryBuilderCall([ + "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", + "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", + "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", "rewhere", + "select", "reselect", "update_all" + ]) and + sink = call.getArgument(0) + or + call = activeRecordQueryBuilderCall("calculate") and + sink = call.getArgument(1) + or + call = + activeRecordQueryBuilderCall(["average", "count", "maximum", "minimum", "sum", "count_by_sql"]) and + sink = call.getArgument(0) + or + // This format was supported until Rails 2.3.8 + call = activeRecordQueryBuilderCall(["all", "find", "first", "last"]) and + sink = call.getKeywordArgument("conditions") + or + call = activeRecordQueryBuilderCall("reload") and + sink = call.getKeywordArgument("lock") + or + // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to + // SQLi if user supplied input is passed in as an argument. + call = activeRecordQueryBuilderCall("annotate") and + sink = call.getArgument(_) + or + call = activeRecordConnectionInstance().getAMethodCall("execute") and + sink = call.getArgument(0) +} + +private predicate sqlFragmentArgument(DataFlow::CallNode call, DataFlow::Node sink) { + exists(DataFlow::Node arg | + sqlFragmentArgumentInner(call, arg) and + sink = [arg, arg.(DataFlow::ArrayLiteralNode).getElement(0)] and + unsafeSqlExpr(sink.asExpr().getExpr()) ) } @@ -162,6 +225,8 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { } /** + * DEPRECATED. Use the `SqlExecution` concept or `ActiveRecordSqlExecutionRange`. + * * A method call that may result in executing unintended user-controlled SQL * queries if the `getSqlFragmentSinkArgument()` expression is tainted by * unsanitized user-controlled input. For example, supposing that `User` is an @@ -175,55 +240,32 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { * as `"') OR 1=1 --"` could result in the application looking up all users * rather than just one with a matching name. */ -class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { - // The SQL fragment argument itself - private Expr sqlFragmentExpr; +deprecated class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { + private DataFlow::CallNode call; PotentiallyUnsafeSqlExecutingMethodCall() { - exists(Expr arg | - arg = sqlFragmentArgument(this) and - unsafeSqlExpr(sqlFragmentExpr) and - ( - sqlFragmentExpr = arg - or - sqlFragmentExpr = arg.(ArrayLiteral).getElement(0) - ) and - // Check that method has not been overridden - not exists(SingletonMethod m | - m.getName() = this.getMethodName() and - m.getOuterScope() = this.getReceiverClass() - ) - ) + call.asExpr().getExpr() = this and sqlFragmentArgument(call, _) } /** * Gets the SQL fragment argument of this method call. */ - Expr getSqlFragmentSinkArgument() { result = sqlFragmentExpr } + Expr getSqlFragmentSinkArgument() { + exists(DataFlow::Node sink | + sqlFragmentArgument(call, sink) and result = sink.asExpr().getExpr() + ) + } } /** - * An `SqlExecution::Range` for an argument to a - * `PotentiallyUnsafeSqlExecutingMethodCall` that may be vulnerable to being - * controlled by user input. + * A SQL execution arising from a call to the ActiveRecord library. */ class ActiveRecordSqlExecutionRange extends SqlExecution::Range { - ActiveRecordSqlExecutionRange() { - exists(PotentiallyUnsafeSqlExecutingMethodCall mc | - this.asExpr().getNode() = mc.getSqlFragmentSinkArgument() - ) - or - this = activeRecordConnectionInstance().getAMethodCall("execute").getArgument(0) and - unsafeSqlExpr(this.asExpr().getExpr()) - } + ActiveRecordSqlExecutionRange() { sqlFragmentArgument(_, this) } override DataFlow::Node getSql() { result = this } } -private API::Node activeRecordConnectionInstance() { - result = activeRecordClassApiNode().getReturn("connection") -} - // TODO: model `ActiveRecord` sanitizers // https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html /** @@ -241,15 +283,8 @@ abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range, override predicate methodCallMayAccessField(string methodName) { // The method is not a built-in, and... not isBuiltInMethodForActiveRecordModelInstance(methodName) and - ( - // ...There is no matching method definition in the class, or... - not exists(this.getClass().getMethod(methodName)) - or - // ...the called method can access a field. - exists(Method m | m = this.getClass().getAPotentialFieldAccessMethod() | - m.getName() = methodName - ) - ) + // ...There is no matching method definition in the class + not exists(this.getClass().getMethod(methodName)) } } @@ -317,21 +352,10 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation } // A `self` reference that may resolve to an active record model object -private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation, - DataFlow::SelfParameterNode -{ +private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation { private ActiveRecordModelClass cls; - ActiveRecordModelClassSelfReference() { - exists(MethodBase m | - m = this.getCallable() and - m.getEnclosingModule() = cls and - m = cls.getAMethod() - ) and - // In a singleton method, `self` refers to the class itself rather than an - // instance of that class - not this.getSelfVariable().getDeclaringScope() instanceof SingletonMethod - } + ActiveRecordModelClassSelfReference() { this = cls.getClassNode().getAnOwnInstanceSelf() } final override ActiveRecordModelClass getClass() { result = cls } } @@ -342,7 +366,7 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta class ActiveRecordInstance extends DataFlow::Node { private ActiveRecordModelInstantiation instantiation; - ActiveRecordInstance() { this = instantiation or instantiation.flowsTo(this) } + ActiveRecordInstance() { this = instantiation.track().getAValueReachableFromSource() } /** Gets the `ActiveRecordModelClass` that this is an instance of. */ ActiveRecordModelClass getClass() { result = instantiation.getClass() } @@ -380,12 +404,12 @@ private module Persistence { /** A call to e.g. `User.create(name: "foo")` */ private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range { CreateLikeCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = - [ - "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", - "find_or_create_by!", "insert", "insert!" - ] + this = + activeRecordBaseClass() + .getAMethodCall([ + "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", + "find_or_create_by!", "insert", "insert!" + ]) } override DataFlow::Node getValue() { @@ -402,8 +426,7 @@ private module Persistence { /** A call to e.g. `User.update(1, name: "foo")` */ private class UpdateLikeClassMethodCall extends DataFlow::CallNode, PersistentWriteAccess::Range { UpdateLikeClassMethodCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = ["update", "update!", "upsert"] + this = activeRecordBaseClass().getAMethodCall(["update", "update!", "upsert"]) } override DataFlow::Node getValue() { @@ -448,10 +471,7 @@ private module Persistence { * ``` */ private class TouchAllCall extends DataFlow::CallNode, PersistentWriteAccess::Range { - TouchAllCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = "touch_all" - } + TouchAllCall() { this = activeRecordQueryBuilderCall("touch_all") } override DataFlow::Node getValue() { result = this.getKeywordArgument("time") } } @@ -461,8 +481,7 @@ private module Persistence { private ExprNodes::ArrayLiteralCfgNode arr; InsertAllLikeCall() { - exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and - this.getMethodName() = ["insert_all", "insert_all!", "upsert_all"] and + this = activeRecordBaseClass().getAMethodCall(["insert_all", "insert_all!", "upsert_all"]) and arr = this.getArgument(0).asExpr() } diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index 0374a54c0c1..a3ee4ebebf5 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -10,6 +10,7 @@ activeRecordInstances | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:13:5:13:40 | call to find_by | | ActiveRecord.rb:13:5:13:46 | call to users | +| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:55:5:57:7 | if ... | | ActiveRecord.rb:55:43:56:40 | then ... | @@ -107,12 +108,14 @@ activeRecordSqlExecutionRanges | ActiveRecord.rb:19:16:19:24 | condition | | ActiveRecord.rb:28:30:28:44 | ...[...] | | ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" | +| ActiveRecord.rb:30:21:30:45 | call to [] | | ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" | | ActiveRecord.rb:31:16:31:21 | <<-SQL | | ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" | | ActiveRecord.rb:46:20:46:32 | ... + ... | | ActiveRecord.rb:52:16:52:28 | "name #{...}" | | ActiveRecord.rb:56:20:56:39 | "username = #{...}" | +| ActiveRecord.rb:68:21:68:44 | ...[...] | | ActiveRecord.rb:106:27:106:76 | "this is an unsafe annotation:..." | activeRecordModelClassMethodCalls | ActiveRecord.rb:2:3:2:17 | call to has_many | @@ -127,7 +130,6 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:31:5:31:35 | call to where | | ActiveRecord.rb:34:5:34:14 | call to where | | ActiveRecord.rb:34:5:34:48 | call to not | -| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | | ActiveRecord.rb:46:5:46:33 | call to delete_by | @@ -135,7 +137,6 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:56:7:56:40 | call to find_by | | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | -| ActiveRecord.rb:68:5:68:45 | call to delete_by | | ActiveRecord.rb:72:5:72:24 | call to create | | ActiveRecord.rb:76:5:76:66 | call to create | | ActiveRecord.rb:80:5:80:68 | call to create | @@ -152,6 +153,96 @@ activeRecordModelClassMethodCalls | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | | associations.rb:16:3:16:18 | call to belongs_to | | associations.rb:19:11:19:20 | call to new | +| associations.rb:21:9:21:21 | call to posts | +| associations.rb:21:9:21:28 | call to create | +| associations.rb:23:12:23:25 | call to comments | +| associations.rb:23:12:23:32 | call to create | +| associations.rb:25:11:25:22 | call to author | +| associations.rb:27:9:27:21 | call to posts | +| associations.rb:27:9:27:28 | call to create | +| associations.rb:29:1:29:13 | call to posts | +| associations.rb:29:1:29:22 | ... << ... | +| associations.rb:31:1:31:12 | call to author= | +| associations.rb:35:1:35:14 | call to comments | +| associations.rb:35:1:35:21 | call to create | +| associations.rb:35:1:35:28 | call to create | +| associations.rb:37:1:37:13 | call to posts | +| associations.rb:37:1:37:20 | call to reload | +| associations.rb:37:1:37:27 | call to create | +| associations.rb:39:1:39:15 | call to build_tag | +| associations.rb:40:1:40:15 | call to build_tag | +| associations.rb:42:1:42:13 | call to posts | +| associations.rb:42:1:42:25 | call to push | +| associations.rb:43:1:43:13 | call to posts | +| associations.rb:43:1:43:27 | call to concat | +| associations.rb:44:1:44:13 | call to posts | +| associations.rb:44:1:44:19 | call to build | +| associations.rb:45:1:45:13 | call to posts | +| associations.rb:45:1:45:20 | call to create | +| associations.rb:46:1:46:13 | call to posts | +| associations.rb:46:1:46:21 | call to create! | +| associations.rb:47:1:47:13 | call to posts | +| associations.rb:47:1:47:20 | call to delete | +| associations.rb:48:1:48:13 | call to posts | +| associations.rb:48:1:48:24 | call to delete_all | +| associations.rb:49:1:49:13 | call to posts | +| associations.rb:49:1:49:21 | call to destroy | +| associations.rb:50:1:50:13 | call to posts | +| associations.rb:50:1:50:25 | call to destroy_all | +| associations.rb:51:1:51:13 | call to posts | +| associations.rb:51:1:51:22 | call to distinct | +| associations.rb:51:1:51:36 | call to find | +| associations.rb:52:1:52:13 | call to posts | +| associations.rb:52:1:52:19 | call to reset | +| associations.rb:52:1:52:33 | call to find | +| associations.rb:53:1:53:13 | call to posts | +| associations.rb:53:1:53:20 | call to reload | +| associations.rb:53:1:53:34 | call to find | +activeRecordModelClassMethodCallsReplacement +| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:2:3:2:17 | call to has_many | +| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:13:5:13:40 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:9:5:9:68 | call to find | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:28:5:28:45 | call to calculate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:29:5:29:43 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:30:5:30:46 | call to destroy_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:31:5:31:35 | call to where | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:34:5:34:14 | call to where | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:35:5:35:51 | call to authenticate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:36:5:36:30 | call to find_by_name | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:46:5:46:33 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:52:5:52:29 | call to order | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:56:7:56:40 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:60:5:60:33 | call to find_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:62:5:62:34 | call to find | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:84:5:84:16 | call to create | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:88:5:88:27 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:92:5:92:69 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:96:5:96:71 | call to update | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:102:13:102:54 | call to annotate | +| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:106:13:106:77 | call to annotate | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:19:5:19:25 | call to destroy_by | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:68:5:68:45 | call to delete_by | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:72:5:72:24 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:76:5:76:66 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:80:5:80:68 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:84:5:84:16 | call to create | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:88:5:88:27 | call to update | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:92:5:92:69 | call to update | +| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:96:5:96:71 | call to update | +| associations.rb:1:1:3:3 | Author | associations.rb:2:3:2:17 | call to has_many | +| associations.rb:1:1:3:3 | Author | associations.rb:19:11:19:20 | call to new | +| associations.rb:5:1:9:3 | Post | associations.rb:6:3:6:20 | call to belongs_to | +| associations.rb:5:1:9:3 | Post | associations.rb:7:3:7:20 | call to has_many | +| associations.rb:5:1:9:3 | Post | associations.rb:8:3:8:31 | call to has_and_belongs_to_many | +| associations.rb:11:1:13:3 | Tag | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | +| associations.rb:15:1:17:3 | Comment | associations.rb:16:3:16:18 | call to belongs_to | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql index 731679e437b..348ca1456e2 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql @@ -9,9 +9,19 @@ query predicate activeRecordInstances(ActiveRecordInstance i) { any() } query predicate activeRecordSqlExecutionRanges(ActiveRecordSqlExecutionRange range) { any() } -query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { any() } +deprecated query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { + any() +} -query predicate potentiallyUnsafeSqlExecutingMethodCall(PotentiallyUnsafeSqlExecutingMethodCall call) { +query predicate activeRecordModelClassMethodCallsReplacement( + ActiveRecordModelClass cls, DataFlow::CallNode call +) { + call = cls.getClassNode().trackModule().getAMethodCall(_) +} + +deprecated query predicate potentiallyUnsafeSqlExecutingMethodCall( + PotentiallyUnsafeSqlExecutingMethodCall call +) { any() } From e3a04499f630e19b90fb0b3aeef2a64c9edb4368 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:07:45 +0200 Subject: [PATCH 15/93] Ruby: minor overhaul of ActiveResource model --- .../codeql/ruby/frameworks/ActiveResource.qll | 196 ++++++++---------- .../active_resource/ActiveResource.expected | 10 + .../active_resource/ActiveResource.ql | 15 +- 3 files changed, 112 insertions(+), 109 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll index 96219915770..9f0e0f4b859 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll @@ -18,8 +18,12 @@ module ActiveResource { * An ActiveResource model class. This is any (transitive) subclass of ActiveResource. */ pragma[nomagic] - private API::Node modelApiNode() { - result = API::getTopLevelMember("ActiveResource").getMember("Base").getASubclass() + private API::Node activeResourceBaseClass() { + result = API::getTopLevelMember("ActiveResource").getMember("Base") + } + + private DataFlow::ClassNode activeResourceClass() { + result = activeResourceBaseClass().getADescendentModule() } /** @@ -30,16 +34,8 @@ module ActiveResource { * end * ``` */ - class ModelClass extends ClassDeclaration { - API::Node model; - - ModelClass() { - model = modelApiNode() and - this.getSuperclassExpr() = model.getAValueReachableFromSource().asExpr().getExpr() - } - - /** Gets the API node for this model */ - API::Node getModelApiNode() { result = model } + class ModelClassNode extends DataFlow::ClassNode { + ModelClassNode() { this = activeResourceClass() } /** Gets a call to `site=`, which sets the base URL for this model. */ SiteAssignCall getASiteAssignment() { result.getModelClass() = this } @@ -49,6 +45,46 @@ module ActiveResource { c = this.getASiteAssignment() and c.disablesCertificateValidation() } + + /** Gets a method call on this class that returns an instance of the class. */ + private DataFlow::CallNode getAChainedCall() { + result.(FindCall).getModelClass() = this + or + result.(CreateCall).getModelClass() = this + or + result.(CustomHttpCall).getModelClass() = this + or + result.(CollectionCall).getCollection().getModelClass() = this and + result.getMethodName() = ["first", "last"] + } + + /** Gets an API node referring to an instance of this class. */ + API::Node getAnInstanceReference() { + result = this.trackInstance() + or + result = this.getAChainedCall().track() + } + } + + /** DEPRECATED. Use `ModelClassNode` instead. */ + deprecated class ModelClass extends ClassDeclaration { + private ModelClassNode cls; + + ModelClass() { this = cls.getADeclaration() } + + /** Gets the class for which this is a declaration. */ + ModelClassNode getClassNode() { result = cls } + + /** Gets the API node for this class object. */ + deprecated API::Node getModelApiNode() { result = cls.trackModule() } + + /** Gets a call to `site=`, which sets the base URL for this model. */ + SiteAssignCall getASiteAssignment() { result = cls.getASiteAssignment() } + + /** Holds if `c` sets a base URL which does not use HTTPS. */ + predicate disablesCertificateValidation(SiteAssignCall c) { + cls.disablesCertificateValidation(c) + } } /** @@ -62,25 +98,20 @@ module ActiveResource { * ``` */ class ModelClassMethodCall extends DataFlow::CallNode { - API::Node model; + private ModelClassNode cls; - ModelClassMethodCall() { - model = modelApiNode() and - this = classMethodCall(model, _) - } + ModelClassMethodCall() { this = cls.trackModule().getAMethodCall(_) } /** Gets the model class for this call. */ - ModelClass getModelClass() { result.getModelApiNode() = model } + ModelClassNode getModelClass() { result = cls } } /** * A call to `site=` on an ActiveResource model class. * This sets the base URL for all HTTP requests made by this class. */ - private class SiteAssignCall extends DataFlow::CallNode { - API::Node model; - - SiteAssignCall() { model = modelApiNode() and this = classMethodCall(model, "site=") } + private class SiteAssignCall extends ModelClassMethodCall { + SiteAssignCall() { this.getMethodName() = "site=" } /** * Gets a node that contributes to the URLs used for HTTP requests by the parent @@ -88,12 +119,10 @@ module ActiveResource { */ DataFlow::Node getAUrlPart() { result = this.getArgument(0) } - /** Gets the model class for this call. */ - ModelClass getModelClass() { result.getModelApiNode() = model } - /** Holds if this site value specifies HTTP rather than HTTPS. */ predicate disablesCertificateValidation() { this.getAUrlPart() + // TODO: We should not need all this just to get the string value .asExpr() .(ExprNodes::AssignExprCfgNode) .getRhs() @@ -141,87 +170,70 @@ module ActiveResource { } /** + * DEPRECATED. Use `ModelClassNode.getAnInstanceReference()` instead. + * * An ActiveResource model object. */ - class ModelInstance extends DataFlow::Node { - ModelClass cls; + deprecated class ModelInstance extends DataFlow::Node { + private ModelClassNode cls; - ModelInstance() { - exists(API::Node model | model = modelApiNode() | - this = model.getInstance().getAValueReachableFromSource() and - cls.getModelApiNode() = model - ) - or - exists(FindCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CreateCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CustomHttpCall call | call.flowsTo(this) | cls = call.getModelClass()) - or - exists(CollectionCall call | - call.getMethodName() = ["first", "last"] and - call.flowsTo(this) - | - cls = call.getCollection().getModelClass() - ) - } + ModelInstance() { this = cls.getAnInstanceReference().getAValueReachableFromSource() } /** Gets the model class for this instance. */ - ModelClass getModelClass() { result = cls } + ModelClassNode getModelClass() { result = cls } } /** * A call to a method on an ActiveResource model object. */ class ModelInstanceMethodCall extends DataFlow::CallNode { - ModelInstance i; + private ModelClassNode cls; - ModelInstanceMethodCall() { this.getReceiver() = i } + ModelInstanceMethodCall() { this = cls.getAnInstanceReference().getAMethodCall(_) } /** Gets the model instance for this call. */ - ModelInstance getInstance() { result = i } + deprecated ModelInstance getInstance() { result = this.getReceiver() } /** Gets the model class for this call. */ - ModelClass getModelClass() { result = i.getModelClass() } + ModelClassNode getModelClass() { result = cls } } /** - * A collection of ActiveResource model objects. + * DEPRECATED. Use `CollectionSource` instead. + * + * A data flow node that may refer to a collection of ActiveResource model objects. */ - class Collection extends DataFlow::Node { - ModelClassMethodCall classMethodCall; + deprecated class Collection extends DataFlow::Node { + Collection() { this = any(CollectionSource src).track().getAValueReachableFromSource() } + } - Collection() { - classMethodCall.flowsTo(this) and - ( - classMethodCall.getMethodName() = "all" - or - classMethodCall.getMethodName() = "find" and - classMethodCall.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") - ) + /** + * A call that returns a collection of ActiveResource model objects. + */ + class CollectionSource extends ModelClassMethodCall { + CollectionSource() { + this.getMethodName() = "all" + or + this.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") } - - /** Gets the model class for this collection. */ - ModelClass getModelClass() { result = classMethodCall.getModelClass() } } /** * A method call on a collection. */ class CollectionCall extends DataFlow::CallNode { - CollectionCall() { this.getReceiver() instanceof Collection } + private CollectionSource collection; + + CollectionCall() { this = collection.track().getAMethodCall(_) } /** Gets the collection for this call. */ - Collection getCollection() { result = this.getReceiver() } + CollectionSource getCollection() { result = collection } } private class ModelClassMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelClassMethodCall { - ModelClass cls; - ModelClassMethodCallAsHttpRequest() { - this.getModelClass() = cls and this.getMethodName() = ["all", "build", "create", "create!", "find", "first", "last"] } @@ -230,12 +242,14 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - cls.disablesCertificateValidation(disablingNode) and + this.getModelClass().disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } + override DataFlow::Node getAUrlPart() { + result = this.getModelClass().getASiteAssignment().getAUrlPart() + } override DataFlow::Node getResponseBody() { result = this } } @@ -243,10 +257,7 @@ module ActiveResource { private class ModelInstanceMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelInstanceMethodCall { - ModelClass cls; - ModelInstanceMethodCallAsHttpRequest() { - this.getModelClass() = cls and this.getMethodName() = [ "exists?", "reload", "save", "save!", "destroy", "delete", "get", "patch", "post", "put", @@ -259,42 +270,15 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - cls.disablesCertificateValidation(disablingNode) and + this.getModelClass().disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } + override DataFlow::Node getAUrlPart() { + result = this.getModelClass().getASiteAssignment().getAUrlPart() + } override DataFlow::Node getResponseBody() { result = this } } - - /** - * A call to a class method. - * - * TODO: is this general enough to be useful elsewhere? - * - * Examples: - * ```rb - * class A - * def self.m; end - * - * m # call - * end - * - * A.m # call - * ``` - */ - private DataFlow::CallNode classMethodCall(API::Node classNode, string methodName) { - // A.m - result = classNode.getAMethodCall(methodName) - or - // class A - // A.m - // end - result.getReceiver().asExpr() instanceof ExprNodes::SelfVariableAccessCfgNode and - result.asExpr().getExpr().getEnclosingModule().(ClassDeclaration).getSuperclassExpr() = - classNode.getAValueReachableFromSource().asExpr().getExpr() and - result.getMethodName() = methodName - } } diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected index a55946c1852..e6d3b056971 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected @@ -33,6 +33,13 @@ modelInstances | active_resource.rb:26:9:26:14 | people | | active_resource.rb:26:9:26:20 | call to first | | active_resource.rb:27:1:27:5 | alice | +modelInstancesAsSource +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:5:9:5:33 | call to new | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:8:9:8:22 | call to find | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:16:1:16:23 | call to new | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:18:1:18:22 | call to get | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:24:10:24:26 | call to find | +| active_resource.rb:1:1:3:3 | Person | active_resource.rb:26:9:26:20 | call to first | modelInstanceMethodCalls | active_resource.rb:6:1:6:10 | call to save | | active_resource.rb:9:1:9:13 | call to address= | @@ -50,3 +57,6 @@ collections | active_resource.rb:24:1:24:26 | ... = ... | | active_resource.rb:24:10:24:26 | call to find | | active_resource.rb:26:9:26:14 | people | +collectionSources +| active_resource.rb:23:10:23:19 | call to all | +| active_resource.rb:24:10:24:26 | call to find | diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql index 1f2fd1efcf1..f1898ddbc98 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql @@ -3,7 +3,8 @@ import codeql.ruby.DataFlow import codeql.ruby.frameworks.ActiveResource query predicate modelClasses( - ActiveResource::ModelClass c, DataFlow::Node siteAssignCall, boolean disablesCertificateValidation + ActiveResource::ModelClassNode c, DataFlow::Node siteAssignCall, + boolean disablesCertificateValidation ) { c.getASiteAssignment() = siteAssignCall and if c.disablesCertificateValidation(siteAssignCall) @@ -13,8 +14,16 @@ query predicate modelClasses( query predicate modelClassMethodCalls(ActiveResource::ModelClassMethodCall c) { any() } -query predicate modelInstances(ActiveResource::ModelInstance c) { any() } +deprecated query predicate modelInstances(ActiveResource::ModelInstance c) { any() } + +query predicate modelInstancesAsSource( + ActiveResource::ModelClassNode cls, DataFlow::LocalSourceNode node +) { + node = cls.getAnInstanceReference().asSource() +} query predicate modelInstanceMethodCalls(ActiveResource::ModelInstanceMethodCall c) { any() } -query predicate collections(ActiveResource::Collection c) { any() } +deprecated query predicate collections(ActiveResource::Collection c) { any() } + +query predicate collectionSources(ActiveResource::CollectionSource c) { any() } From ce0073b30c7bad217de5c62431ec6535645dce25 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:13:07 +0200 Subject: [PATCH 16/93] Ruby: update StoredXSS test results These results were previously flagged for the wrong reason. Calls to a user-define method were seen as ORM calls. The real source is inside the user-defined method, but we miss that due to lack of 'self' handling in ORM tracking. --- .../query-tests/security/cwe-079/StoredXSS.expected | 10 ---------- .../cwe-079/app/views/foo/stores/show.html.erb | 6 +++--- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected index 0eaf24029ef..460c7da3145 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected @@ -3,7 +3,6 @@ edges | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | -| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:5:9:5:21 | call to local_assigns [element :display_text] | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:9:9:9:21 | call to local_assigns [element :display_text] | @@ -22,7 +21,6 @@ nodes | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | semmle.label | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | semmle.label | call to read | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | semmle.label | dt | -| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | semmle.label | call to raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | semmle.label | dt | | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | | app/views/foo/bars/_widget.html.erb:8:9:8:21 | call to local_assigns [element :display_text] | semmle.label | call to local_assigns [element :display_text] | @@ -39,11 +37,7 @@ nodes | app/views/foo/stores/show.html.erb:40:64:40:87 | ... + ... | semmle.label | ... + ... | | app/views/foo/stores/show.html.erb:40:76:40:87 | call to display_text | semmle.label | call to display_text | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | semmle.label | call to handle | -| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | semmle.label | call to raw_name | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | semmle.label | call to handle | -| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | semmle.label | call to raw_name | -| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | semmle.label | call to display_name | -| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | semmle.label | @other_user_raw_name | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | semmle.label | call to sprintf | | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | semmle.label | call to handle | subpaths @@ -57,9 +51,5 @@ subpaths | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | stored value | -| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | stored value | -| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | stored value | -| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | stored value | -| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | stored value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb index 29656a15a3d..d8afec1c432 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb @@ -63,7 +63,7 @@ some_user.handle.html_safe %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= some_user = User.find 1 some_user.raw_name.html_safe @@ -75,10 +75,10 @@ some_user.handle %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= @user.display_name.html_safe %> -<%# BAD: Indirect to a database value without escaping %> +<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> <%= @other_user_raw_name.html_safe %> <%# BAD: Kernel.sprintf is a taint-step %> From f392af220b0ae2562260a81140e28db2cd799964 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:13:31 +0200 Subject: [PATCH 17/93] Ruby: benign changes to SQLi tests (fixed FNs) --- .../query-tests/security/cwe-089/SqlInjection.expected | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 0cc0d213dcc..161cdcc7751 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -22,6 +22,7 @@ edges | ActiveRecordInjection.rb:70:38:70:50 | ...[...] | ActiveRecordInjection.rb:8:31:8:34 | pass | | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | +| ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | @@ -35,6 +36,7 @@ edges | ActiveRecordInjection.rb:103:11:103:17 | ...[...] | ActiveRecordInjection.rb:103:5:103:7 | uid | | ActiveRecordInjection.rb:104:5:104:9 | uidEq | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | +| ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:20:22:20:30 | condition | | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | @@ -102,6 +104,8 @@ nodes | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | | ActiveRecordInjection.rb:74:41:74:46 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:79:23:79:28 | call to params | semmle.label | call to params | +| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | semmle.label | call to params | @@ -123,6 +127,7 @@ nodes | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | semmle.label | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | +| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | | ActiveRecordInjection.rb:155:59:155:64 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | semmle.label | ...[...] | @@ -172,6 +177,7 @@ subpaths | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | ActiveRecordInjection.rb:62:21:62:26 | call to params | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | This SQL query depends on a $@. | ActiveRecordInjection.rb:62:21:62:26 | call to params | user-provided value | | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:68:34:68:39 | call to params | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:68:34:68:39 | call to params | user-provided value | | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:74:41:74:46 | call to params | user-provided value | +| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:79:23:79:28 | call to params | user-provided value | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:83:17:83:22 | call to params | user-provided value | | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:84:19:84:24 | call to params | user-provided value | | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | user-provided value | @@ -179,6 +185,7 @@ subpaths | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | ActiveRecordInjection.rb:94:18:94:23 | call to params | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:94:18:94:23 | call to params | user-provided value | | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | ActiveRecordInjection.rb:96:23:96:28 | call to params | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:96:23:96:28 | call to params | user-provided value | | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | ActiveRecordInjection.rb:102:10:102:15 | call to params | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:102:10:102:15 | call to params | user-provided value | +| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:141:21:141:26 | call to params | user-provided value | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:155:59:155:64 | call to params | user-provided value | | ActiveRecordInjection.rb:168:37:168:41 | query | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:168:37:168:41 | query | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | @@ -189,4 +196,4 @@ subpaths | PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | \ No newline at end of file +| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | From 8539db07c4c825b9980d79946a1121fdb7996679 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 19 Jun 2023 12:14:48 +0200 Subject: [PATCH 18/93] Ruby: Update ActiveDispatch due to change in toString --- .../action_dispatch/ActionDispatch.expected | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected index 71327350941..4eacd48bd60 100644 --- a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected @@ -55,12 +55,12 @@ underscore | LotsOfCapitalLetters | lots_of_capital_letters | | invalid | invalid | mimeTypeInstances -| mime_type.rb:2:6:2:28 | Use getMember("Mime").getContent(element_text/html) | -| mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() | -| mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() | -| mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() | -| mime_type.rb:6:6:6:47 | Use getMember("Mime").getMember("Type").getMethod("register").getReturn() | -| mime_type.rb:7:6:7:64 | Use getMember("Mime").getMember("Type").getMethod("register_alias").getReturn() | +| mime_type.rb:2:6:2:28 | ForwardNode(call to fetch) | +| mime_type.rb:3:6:3:32 | ForwardNode(call to new) | +| mime_type.rb:4:6:4:35 | ForwardNode(call to lookup) | +| mime_type.rb:5:6:5:43 | ForwardNode(call to lookup_by_extension) | +| mime_type.rb:6:6:6:47 | ForwardNode(call to register) | +| mime_type.rb:7:6:7:64 | ForwardNode(call to register_alias) | mimeTypeMatchRegExpInterpretations | mime_type.rb:11:11:11:19 | "foo/bar" | | mime_type.rb:12:7:12:15 | "foo/bar" | From d07e2862f9906917e23167458e3c354dd3739ca6 Mon Sep 17 00:00:00 2001 From: Tony Torralba Date: Thu, 22 Jun 2023 17:36:54 +0200 Subject: [PATCH 19/93] Java: Add URL.toString summary This adds coverage for CVE-2023-35149. --- java/ql/lib/change-notes/2023-06-22-url-tostring-model.md | 4 ++++ java/ql/lib/ext/java.net.model.yml | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 java/ql/lib/change-notes/2023-06-22-url-tostring-model.md diff --git a/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md b/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md new file mode 100644 index 00000000000..fc5a58ce4e6 --- /dev/null +++ b/java/ql/lib/change-notes/2023-06-22-url-tostring-model.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added a missing summary model for the method `java.net.URL.toString`. diff --git a/java/ql/lib/ext/java.net.model.yml b/java/ql/lib/ext/java.net.model.yml index 39a4c484112..24591459432 100644 --- a/java/ql/lib/ext/java.net.model.yml +++ b/java/ql/lib/ext/java.net.model.yml @@ -45,7 +45,8 @@ extensions: - ["java.net", "URI", False, "toURL", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URL", False, "URL", "(String)", "", "Argument[0]", "Argument[this]", "taint", "manual"] - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[0]", "Argument[this]", "taint", "ai-manual"] - - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"] # @atorralba: review for consistency + - ["java.net", "URL", False, "URL", "(URL,String)", "", "Argument[1]", "Argument[this]", "taint", "ai-manual"] - ["java.net", "URL", False, "toExternalForm", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URL", False, "toURI", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] + - ["java.net", "URL", False, "toString", "", "", "Argument[this]", "ReturnValue", "taint", "manual"] - ["java.net", "URLDecoder", False, "decode", "", "", "Argument[0]", "ReturnValue", "taint", "manual"] From 0d05f50aaaf7fddda7e1f3e233691df73e9c8cc4 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Thu, 22 Jun 2023 18:12:13 +0100 Subject: [PATCH 20/93] Kotlin: Remove an expected-no-getter exception We're not sure why it was necessary. --- java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index b37d46080a6..6e5d921a406 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -1559,7 +1559,7 @@ open class KotlinFileExtractor( val setter = p.setter if (getter == null) { - if (p.modality != Modality.FINAL || !isExternalDeclaration(p)) { + if (!isExternalDeclaration(p)) { logger.warnElement("IrProperty without a getter", p) } } else if (shouldExtractDecl(getter, extractPrivateMembers)) { From 2dc4f23dbbc311bde84bacbcc71d5ff217892264 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Fri, 23 Jun 2023 19:34:21 +0200 Subject: [PATCH 21/93] Add models for `org.apache.commons.lang` --- .../org.apache.commons.lang.model.yml | 1695 +++++++++++++++++ .../2023-06-23-apache-commons-lang.md | 4 + 2 files changed, 1699 insertions(+) create mode 100644 java/ql/lib/ext/generated/org.apache.commons.lang.model.yml create mode 100644 java/ql/src/change-notes/2023-06-23-apache-commons-lang.md diff --git a/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml b/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml new file mode 100644 index 00000000000..56f9c251388 --- /dev/null +++ b/java/ql/lib/ext/generated/org.apache.commons.lang.model.yml @@ -0,0 +1,1695 @@ +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of models for the org.apache.commons.lang framework. + +extensions: + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object,Object,Comparator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object[],Object[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(Object[],Object[],Comparator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(boolean,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(boolean[],boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(byte,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(byte[],byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(char[],char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(double,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(double[],double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(float,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(float[],float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(int[],int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(long,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(long[],long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(short,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "append", "(short[],short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", true, "appendSuper", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(Object,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(Object[],Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(boolean,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(boolean[],boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(byte,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(byte[],byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(char[],char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(double,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(double[],double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(float,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(float[],float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(int[],int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(long,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(long[],long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(short,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "append", "(short[],short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", true, "appendSuper", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "append", "(short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", true, "appendSuper", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "ReflectionToStringBuilder", "(Object,ToStringStyle,StringBuffer,Class,boolean,boolean)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "getExcludeFieldNames", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "reflectionAppendArray", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "setExcludeFieldNames", "(String[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "ToStringBuilder", "(Object,ToStringStyle,StringBuffer)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object[])", "", "Argument[0].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[1].ArrayElement", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,Object[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,boolean[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,byte[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,char[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,double[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,float[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,int[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,long[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[],boolean)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(String,short[],boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(boolean[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(byte)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(byte[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(double[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(float[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(int[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(long[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(short)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "append", "(short[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendAsObjectToString", "(Object)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendSuper", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendSuper", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendToString", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "appendToString", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getObject", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getStringBuffer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "getStyle", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[2]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object,Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[2].ArrayElement", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,Object[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,boolean[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,byte[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,char[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,double[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,float[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,int[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,long[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short[],Boolean)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "append", "(StringBuffer,String,short[],Boolean)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendEnd", "(StringBuffer,Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendStart", "(StringBuffer,Object)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendSuper", "(StringBuffer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendSuper", "(StringBuffer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendToString", "(StringBuffer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "appendToString", "(StringBuffer,String)", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArrayEnd", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArraySeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getArrayStart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getContentEnd", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getContentStart", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getFieldNameValueSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getFieldSeparator", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getNullText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSizeEndText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSizeStartText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSummaryObjectEndText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "getSummaryObjectStartText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArrayEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArraySeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setArrayStart", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setContentEnd", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setContentStart", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setFieldNameValueSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setFieldSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setNullText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSizeEndText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSizeStartText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSummaryObjectEndText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", true, "setSummaryObjectStartText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", true, "getName", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.enums", "ValuedEnum", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getCause", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getCause", "(Throwable,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getMessage", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCause", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCauseMessage", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getRootCauseStackTrace", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getThrowableList", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", true, "getThrowables", "(Throwable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getCause", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getMessage", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getMessage", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getThrowable", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", true, "getThrowables", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "NestableDelegate", "(Nestable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessage", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessage", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getMessages", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getThrowable", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", true, "getThrowables", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", true, "NestableError", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", true, "NestableError", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableException", true, "NestableException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableRuntimeException", true, "NestableRuntimeException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "abs", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "pow", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", false, "reduce", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", true, "getValue", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", true, "setValue", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableObject", true, "MutableObject", "(Object)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", true, "getAccessibleConstructor", "(Constructor)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", true, "getAccessibleMethod", "(Method)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "CompositeFormat", "(Format,Format)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "CompositeFormat", "(Format,Format)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "getFormatter", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", true, "getParser", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Locale,Map)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Map)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "ExtendedMessageFormat", true, "ExtendedMessageFormat", "(String,Map)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "StrBuilder", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StrBuilder,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(String,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer,int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(StringBuffer,int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[],int,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(char[],int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "append", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Collection)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Iterator)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendAll", "(Object[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadLeft", "(Object,int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadLeft", "(int,int,char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadRight", "(Object,int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendFixedWidthPadRight", "(int,int,char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendNewLine", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendNull", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendPadding", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(String,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendSeparator", "(char,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Collection,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Collection,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Iterator,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Iterator,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Object[],String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendWithSeparators", "(Object[],String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(boolean)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "appendln", "(long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "clear", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "delete", "(int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteAll", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteCharAt", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "deleteFirst", "(char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "ensureCapacity", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[this]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(char[])", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getChars", "(int,int,char[],int)", "", "Argument[this]", "Argument[2]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getNewLineText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "getNullText", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,Object)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[])", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[],int,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,char[],int,int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,double)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,float)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "insert", "(int,long)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "leftString", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "midString", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "minimizeCapacity", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(StrMatcher,String,int,int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(int,int,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replace", "(int,int,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(StrMatcher,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceAll", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(StrMatcher,String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(String,String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "replaceFirst", "(char,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "reverse", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "rightString", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setCharAt", "(int,char)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setLength", "(int)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNewLineText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNewLineText", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNullText", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "setNullText", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "substring", "(int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "substring", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toCharArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toCharArray", "(int,int)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "toStringBuffer", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", true, "trim", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", true, "mapLookup", "(Map)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "charSetMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "charSetMatcher", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", true, "stringMatcher", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[0].Element", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(Map,String,String,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,StrMatcher,StrMatcher,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "StrSubstitutor", "(StrLookup,String,String,char)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariablePrefixMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariableResolver", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "getVariableSuffixMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StrBuilder)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StrBuilder,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StringBuffer)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(StringBuffer,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "replace", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefix", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefixMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariablePrefixMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableResolver", "(StrLookup)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffix", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffixMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", true, "setVariableSuffixMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,StrMatcher,StrMatcher)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(String,char,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],StrMatcher,StrMatcher)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],String)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "StrTokenizer", "(char[],char,char)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getCSVInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getCSVInstance", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getContent", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getDelimiterMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getIgnoredMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getQuoteMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTSVInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTSVInstance", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTokenArray", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTokenList", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "getTrimmerMatcher", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "nextToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "previousToken", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "()", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(String)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(char[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "reset", "(char[])", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setDelimiterString", "(String)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setEmptyTokenAsNull", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoreEmptyTokens", "(boolean)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setIgnoredMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteChar", "(char)", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setQuoteMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setTrimmerMatcher", "(StrMatcher)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "setTrimmerMatcher", "(StrMatcher)", "", "Argument[this]", "ReturnValue", "value", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "iterator", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "iterator", "(Object,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "round", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", true, "truncate", "(Calendar,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(Calendar,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(Date,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "format", "(long,StringBuffer)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateInstance", "(int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getDateTimeInstance", "(int,int,TimeZone,Locale)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getInstance", "(String,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getLocale", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getPattern", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeInstance", "(int,TimeZone,Locale)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "getTimeZone", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(Object[],int,Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(byte[],byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(byte[],int,byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(char[],char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "add", "(char[],int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(Object[],Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(Object[],Object[])", "", "Argument[1].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(byte[],byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(byte[],byte[])", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(char[],char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "addAll", "(char[],char[])", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "clone", "(char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(Object[],int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(byte[],int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "remove", "(char[],int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(Object[],Object)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(byte[],byte)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "removeElement", "(char[],char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(Object[],int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(byte[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "subarray", "(char[],int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toMap", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", true, "toString", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(Boolean,String,String,String)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(boolean,String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", true, "toString", "(boolean,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharRange", false, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSet", true, "getCharRanges", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "delete", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "delete", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "squeeze", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "squeeze", "(String,String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", true, "translate", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageCanonicalName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageCanonicalName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getPackageName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortCanonicalName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortCanonicalName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortClassName", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "getShortClassName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "primitivesToWrappers", "(Class[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", true, "wrappersToPrimitives", "(Class[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", true, "IllegalClassException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "IncompleteArgumentException", true, "IncompleteArgumentException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "IncompleteArgumentException", true, "IncompleteArgumentException", "(String,String[])", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale,Locale)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", true, "localeLookupList", "(Locale,Locale)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", true, "NotImplementedException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "NullArgumentException", true, "NullArgumentException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "appendIdentityToString", "(StringBuffer,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "defaultIfNull", "(Object,Object)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "defaultIfNull", "(Object,Object)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "max", "(Comparable,Comparable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "max", "(Comparable,Comparable)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "min", "(Comparable,Comparable)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "min", "(Comparable,Comparable)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", true, "toString", "(Object,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationException", true, "SerializationException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", true, "deserialize", "(InputStream)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", true, "deserialize", "(byte[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "escapeSql", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeCsv", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeCsv", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeHtml", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeHtml", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeXml", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", true, "unescapeXml", "(Writer,String)", "", "Argument[1]", "Argument[0]", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "abbreviate", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "abbreviate", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitalise", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitaliseAllWords", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "capitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "center", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chomp", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chompLast", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chompLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chop", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "chopNewline", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "clean", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "concatenate", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultIfEmpty", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultIfEmpty", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "defaultString", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "deleteSpaces", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "deleteWhitespace", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "difference", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "difference", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getChomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getChomp", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getCommonPrefix", "(String[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getNestedString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getNestedString", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "getPrechomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Collection,char)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,String)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Iterator,char)", "", "Argument[0].Element", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String,int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],char)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "join", "(Object[],char,int,int)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "left", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "leftPad", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "lowerCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "mid", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlay", "(String,String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlay", "(String,String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlayString", "(String,String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "overlayString", "(String,String,int,int)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "prechomp", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "remove", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "remove", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeEnd", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeEndIgnoreCase", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeStart", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "removeStartIgnoreCase", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "repeat", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replace", "(String,String,String,int)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceChars", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceChars", "(String,char,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEach", "(String,String[],String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEach", "(String,String[],String[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEachRepeatedly", "(String,String[],String[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceEachRepeatedly", "(String,String[],String[])", "", "Argument[2].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceOnce", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "replaceOnce", "(String,String,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverse", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimited", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimitedString", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "reverseDelimitedString", "(String,String)", "", "Argument[1]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "right", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,String)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "rightPad", "(String,int,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "split", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByCharacterType", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByCharacterTypeCamelCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparator", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparator", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparatorPreserveAllTokens", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitByWholeSeparatorPreserveAllTokens", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "splitPreserveAllTokens", "(String,char)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "strip", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "strip", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripAll", "(String[])", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripAll", "(String[],String)", "", "Argument[0].ArrayElement", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripEnd", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripStart", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripToEmpty", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "stripToNull", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substring", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substring", "(String,int,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringAfter", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringAfterLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBefore", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBeforeLast", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBetween", "(String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringBetween", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "substringsBetween", "(String,String,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "swapCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trim", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trimToEmpty", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "trimToNull", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "uncapitalise", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "uncapitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", true, "upperCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "UnhandledException", true, "UnhandledException", "(Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "abbreviate", "(String,int,int,String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "abbreviate", "(String,int,int,String)", "", "Argument[3]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalize", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalizeFully", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "capitalizeFully", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "initials", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "initials", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "swapCase", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "uncapitalize", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "uncapitalize", "(String,char[])", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int,String,boolean)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["org.apache.commons.lang", "WordUtils", true, "wrap", "(String,int,String,boolean)", "", "Argument[2]", "ReturnValue", "taint", "df-generated"] + + + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "reflectionCompare", "(Object,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "CompareToBuilder", "toComparison", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "isEquals", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "EqualsBuilder", "reflectionEquals", "(Object,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "HashCodeBuilder", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "reflectionHashCode", "(int,int,Object,boolean,Class,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "HashCodeBuilder", "toHashCode", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "getUpToClass", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "isAppendStatics", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "isAppendTransients", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setAppendStatics", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setAppendTransients", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "setUpToClass", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toString", "(Object,ToStringStyle,boolean,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ReflectionToStringBuilder", "toStringExclude", "(Object,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "getDefaultStyle", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "reflectionToString", "(Object,ToStringStyle,boolean,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringBuilder", "setDefaultStyle", "(ToStringStyle)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isArrayContentDetail", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isDefaultFullDetail", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isFieldSeparatorAtEnd", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isFieldSeparatorAtStart", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isShortClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseFieldNames", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseIdentityHashCode", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "isUseShortClassName", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setArrayContentDetail", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setDefaultFullDetail", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setFieldSeparatorAtEnd", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setFieldSeparatorAtStart", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setShortClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseFieldNames", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseIdentityHashCode", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.builder", "ToStringStyle", "setUseShortClassName", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "Enum", "getEnumClass", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnum", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnum", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnumList", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "getEnumMap", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "EnumUtils", "iterator", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.enums", "ValuedEnum", "getValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "addCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getFullStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getStackFrames", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "getThrowableCount", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfThrowable", "(Throwable,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfThrowable", "(Throwable,Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfType", "(Throwable,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "indexOfType", "(Throwable,Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isNestedThrowable", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "isThrowableNested", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable,PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "printRootCauseStackTrace", "(Throwable,PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "removeCauseMethodName", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "removeCommonFrames", "(List,List)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "ExceptionUtils", "setCause", "(Throwable,Throwable)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "getMessages", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "getThrowableCount", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "indexOfThrowable", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "indexOfThrowable", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printPartialStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printStackTrace", "(PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "Nestable", "printStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "getThrowableCount", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "indexOfThrowable", "(Class,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "(PrintStream)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableDelegate", "printStackTrace", "(PrintWriter)", "summary", "df-generated"] + - ["org.apache.commons.lang.exception", "NestableError", "NestableError", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "DoubleRange", "DoubleRange", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "FloatRange", "FloatRange", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "add", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "divideBy", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getDenominator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getFraction", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getNumerator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getProperNumerator", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getProperWhole", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "getReducedFraction", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "invert", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "multiplyBy", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "negate", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "subtract", "(Fraction)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "toProperString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Fraction", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "max", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IEEE754rUtils", "min", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "IntRange", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "IntRange", "toArray", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "JVMRandom", "nextLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "LongRange", "(long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "LongRange", "toArray", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", "NumberRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberRange", "NumberRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "compare", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "compare", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createBigDecimal", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createBigInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "createNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "isDigits", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "isNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(byte,byte,byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(short,short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "max", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(byte,byte,byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(double,double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(float,float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(short,short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "min", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "stringToInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "stringToInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toDouble", "(String,double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toFloat", "(String,float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "NumberUtils", "toLong", "(String,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextBoolean", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextBoolean", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextDouble", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextFloat", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(Random,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextInt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "RandomUtils", "nextLong", "(Random)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsDouble", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsDouble", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsFloat", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsFloat", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsInteger", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsInteger", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsLong", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsNumber", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "containsRange", "(Range)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMaximumNumber", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "getMinimumNumber", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "overlapsRange", "(Range)", "summary", "df-generated"] + - ["org.apache.commons.lang.math", "Range", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", "getValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "Mutable", "setValue", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "MutableBoolean", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "MutableBoolean", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "booleanValue", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "setValue", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableBoolean", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "MutableByte", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "MutableByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "add", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "setValue", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "subtract", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "toByte", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableByte", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "MutableDouble", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "MutableDouble", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "add", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "isInfinite", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "isNaN", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "setValue", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "subtract", "(double)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "toDouble", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableDouble", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "MutableFloat", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "MutableFloat", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "add", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "isInfinite", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "isNaN", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "setValue", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "subtract", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "toFloat", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableFloat", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "MutableInt", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "MutableInt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "add", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "setValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "subtract", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "toInteger", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableInt", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "MutableLong", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "MutableLong", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "add", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "setValue", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "subtract", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "toLong", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableLong", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableObject", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "MutableShort", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "MutableShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "add", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "add", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "decrement", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "increment", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "setValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "subtract", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "subtract", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "toShort", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.mutable", "MutableShort", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getAccessibleConstructor", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getAccessibleConstructor", "(Class,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "getMatchingAccessibleConstructor", "(Class,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeConstructor", "(Class,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "ConstructorUtils", "invokeExactConstructor", "(Class,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getDeclaredField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getDeclaredField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "getField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredField", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredField", "(Object,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredStaticField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readDeclaredStaticField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Field,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Field,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Object,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readField", "(Object,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Class,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Field)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "readStaticField", "(Field,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredField", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredField", "(Object,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredStaticField", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeDeclaredStaticField", "(Class,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Field,Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Field,Object,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeField", "(Object,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Class,String,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Field,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "FieldUtils", "writeStaticField", "(Field,Object,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "clearCache", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getAccessibleMethod", "(Class,String,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getAccessibleMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "getMatchingAccessibleMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactMethod", "(Object,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeExactStaticMethod", "(Class,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeMethod", "(Object,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "invokeStaticMethod", "(Class,String,Object[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang.reflect", "MethodUtils", "setCacheMethods", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "CompositeFormat", "reformat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "StrBuilder", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StrBuilder,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(String,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StringBuffer)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(StringBuffer,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "appendln", "(char[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asReader", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asTokenizer", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "asWriter", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "capacity", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "charAt", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "endsWith", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "equals", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "equalsIgnoreCase", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(StrMatcher,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "indexOf", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "isEmpty", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(StrMatcher)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(StrMatcher,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "lastIndexOf", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "length", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "size", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrBuilder", "startsWith", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "lookup", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "noneLookup", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrLookup", "systemPropertiesLookup", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "charMatcher", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "commaMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "doubleQuoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "isMatch", "(char[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "isMatch", "(char[],int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "noneMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "quoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "singleQuoteMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "spaceMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "splitMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "tabMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrMatcher", "trimMatcher", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "getEscapeChar", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object,Map)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replace", "(Object,Map,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StrBuilder)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StrBuilder,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StringBuffer)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceIn", "(StringBuffer,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "replaceSystemProperties", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrSubstitutor", "setEscapeChar", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "getCSVInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "getTSVInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "isEmptyTokenAsNull", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "isIgnoreEmptyTokens", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.text", "StrTokenizer", "size", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Calendar,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(Date,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "format", "(long,String,TimeZone,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(Date,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(Date,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateFormatUtils", "formatUTC", "(long,String,Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "add", "(Date,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addMonths", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addWeeks", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "addYears", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInDays", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInHours", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMilliseconds", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMinutes", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInSeconds", "(Calendar,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "getFragmentInSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameDay", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameDay", "(Date,Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameInstant", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameInstant", "(Date,Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "isSameLocalTime", "(Calendar,Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "iterator", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "parseDate", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "round", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "round", "(Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setDays", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setHours", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMilliseconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMinutes", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setMonths", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setSeconds", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "setYears", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "truncate", "(Date,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DateUtils", "truncate", "(Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDuration", "(long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDuration", "(long,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationHMS", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationISO", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatDurationWords", "(long,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriod", "(long,long,String)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriod", "(long,long,String,boolean,TimeZone)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "DurationFormatUtils", "formatPeriodISO", "(long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(Calendar)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(Date)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "format", "(long)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getDateInstance", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getDateTimeInstance", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getInstance", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getMaxLengthEstimate", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getTimeInstance", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "FastDateFormat", "getTimeZoneOverridesCalendar", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getSplitTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getStartTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "getTime", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "reset", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "resume", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "split", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "start", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "stop", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "suspend", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "toSplitString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang.time", "StopWatch", "unsplit", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(boolean[],int,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(double[],int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(float[],int,float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(long[],int,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(short[],int,short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "add", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(boolean[],boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(double[],double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(float[],float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(int[],int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(long[],long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "addAll", "(short[],short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "clone", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "contains", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "getLength", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "hashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(Object[],Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(boolean[],boolean,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(byte[],byte,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(char[],char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(double[],double,int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(float[],float,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(long[],long,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "indexOf", "(short[],short,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEmpty", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isEquals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(Object[],Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(boolean[],boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(byte[],byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(char[],char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(double[],double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(float[],float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(int[],int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(long[],long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameLength", "(short[],short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "isSameType", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(Object[],Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(Object[],Object,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(boolean[],boolean,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(byte[],byte,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(char[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(char[],char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(double[],double,int,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(float[],float,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(long[],long,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "lastIndexOf", "(short[],short,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(boolean[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(double[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(float[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(long[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "remove", "(short[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(int[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "removeElement", "(short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "reverse", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(boolean[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(double[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(float[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(int[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(long[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "subarray", "(short[],int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(int[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toObject", "(short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Boolean[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Byte[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Byte[],byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Character[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Character[],char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Double[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Double[],double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Float[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Float[],float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Integer[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Integer[],int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Long[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Long[],long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Short[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ArrayUtils", "toPrimitive", "(Short[],short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "BitField", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clear", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clearByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "clearShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getRawValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getShortRawValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getShortValue", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "getValue", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "isAllSet", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "isSet", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "set", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setBoolean", "(int,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setByte", "(byte)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setByteBoolean", "(byte,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShort", "(short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShortBoolean", "(short,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setShortValue", "(short,short)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BitField", "setValue", "(int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isNotFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isNotTrue", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "isTrue", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "negate", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(String,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBoolean", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanDefaultIfNull", "(Boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(Integer,Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(String,String,String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toBooleanObject", "(int,int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(Boolean,int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toInteger", "(boolean,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(Boolean,Integer,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toIntegerObject", "(boolean,Integer,Integer)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringOnOff", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringOnOff", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringTrueFalse", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringTrueFalse", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringYesNo", "(Boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "toStringYesNo", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "xor", "(Boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "BooleanUtils", "xor", "(boolean[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharEncoding", "isSupported", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "CharRange", "(char,char,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "contains", "(CharRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "getEnd", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "getStart", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharRange", "isNegated", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "contains", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "getInstance", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "getInstance", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSet", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "count", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "count", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "evaluateSet", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "keep", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharSetUtils", "keep", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAscii", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlpha", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphaLower", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphaUpper", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiAlphanumeric", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiControl", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiNumeric", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "isAsciiPrintable", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(Character,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toChar", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toCharacterObject", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toCharacterObject", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(Character,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toIntValue", "(char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toString", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "toString", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "unicodeEscaped", "(Character)", "summary", "df-generated"] + - ["org.apache.commons.lang", "CharUtils", "unicodeEscaped", "(char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "convertClassNamesToClasses", "(List)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "convertClassesToClassNames", "(List)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getAllInterfaces", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getAllSuperclasses", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(ClassLoader,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(ClassLoader,String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getClass", "(String,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPackageCanonicalName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPackageName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getPublicMethod", "(Class,String,Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getShortCanonicalName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "getShortClassName", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class,Class,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class[],Class[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isAssignable", "(Class[],Class[],boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "isInnerClass", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "primitiveToWrapper", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "toClass", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "ClassUtils", "wrapperToPrimitive", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", "IllegalClassException", "(Class,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "IllegalClassException", "IllegalClassException", "(Class,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "availableLocaleList", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "availableLocaleSet", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "countriesByLanguage", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "isAvailableLocale", "(Locale)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "languagesByCountry", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "LocaleUtils", "toLocale", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NotImplementedException", "NotImplementedException", "(Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "NumberRange", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "NumberRange", "(Number,Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "getMaximum", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "getMinimum", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "includesNumber", "(Number)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "includesRange", "(NumberRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "overlaps", "(NumberRange)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberRange", "toString", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "compare", "(double,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "compare", "(float,float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createBigDecimal", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createBigInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createDouble", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createFloat", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createInteger", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createLong", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "createNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "isDigits", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "isNumber", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "maximum", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "maximum", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "minimum", "(int,int,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "minimum", "(long,long,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "stringToInt", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "NumberUtils", "stringToInt", "(String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "equals", "(Object,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "hashCode", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "identityToString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "identityToString", "(StringBuffer,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "ObjectUtils", "toString", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "random", "(int,int,int,boolean,boolean,char[],Random)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAlphabetic", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAlphanumeric", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomAscii", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "RandomStringUtils", "randomNumeric", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "clone", "(Serializable)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "serialize", "(Serializable)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SerializationUtils", "serialize", "(Serializable,OutputStream)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeCsv", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeCsv", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeHtml", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeHtml", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJava", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJava", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJavaScript", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeJavaScript", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeXml", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "escapeXml", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJava", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJava", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJavaScript", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringEscapeUtils", "unescapeJavaScript", "(Writer,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "contains", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "contains", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsAny", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsAny", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsNone", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsNone", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsOnly", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "containsOnly", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "countMatches", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "endsWith", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "endsWithIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "equals", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "equalsIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "escape", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "getLevenshteinDistance", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOf", "(String,char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAny", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAnyBut", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfAnyBut", "(String,char[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfDifference", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "indexOfDifference", "(String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlpha", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphaSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphanumeric", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAlphanumericSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isAsciiPrintable", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isBlank", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNotBlank", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNotEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNumeric", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isNumericSpace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "isWhitespace", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,char)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOf", "(String,char,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "lastIndexOfAny", "(String,String[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "length", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "ordinalIndexOf", "(String,String,int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "startsWith", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "StringUtils", "startsWithIgnoreCase", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaHome", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaIoTmpDir", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getJavaVersion", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getUserDir", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "getUserHome", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaAwtHeadless", "()", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaVersionAtLeast", "(float)", "summary", "df-generated"] + - ["org.apache.commons.lang", "SystemUtils", "isJavaVersionAtLeast", "(int)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "allElementsOfType", "(Collection,Class)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "allElementsOfType", "(Collection,Class,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,double)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "isTrue", "(boolean,String,long)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Collection,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "noNullElements", "(Object[],String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Collection)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Collection,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Map)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Map,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Object[])", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(Object[],String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notEmpty", "(String,String)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notNull", "(Object)", "summary", "df-generated"] + - ["org.apache.commons.lang", "Validate", "notNull", "(Object,String)", "summary", "df-generated"] diff --git a/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md b/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md new file mode 100644 index 00000000000..dc33878d2e5 --- /dev/null +++ b/java/ql/src/change-notes/2023-06-23-apache-commons-lang.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* New models have been added for `org.apache.commons.lang`. From fba753ce61c95513b5d3f537671d1b46902e5786 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Sat, 24 Jun 2023 18:59:25 +0100 Subject: [PATCH 22/93] C++: Add more test cases to 'cpp/invalid-pointer-deref'. --- .../InvalidPointerDeref.expected | 63 +++++++++ .../CWE/CWE-193/pointer-deref/test.cpp | 121 ++++++++++++++++++ 2 files changed, 184 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 7c27659de1f..121edbaa801 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 @@ -788,6 +788,29 @@ edges | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:6 | xs | | test.cpp:433:5:433:6 | xs | test.cpp:433:5:433:17 | access to array | | test.cpp:433:5:433:17 | access to array | test.cpp:433:5:433:21 | Store: ... = ... | +| test.cpp:439:14:439:27 | new[] | test.cpp:444:5:444:6 | xs | +| test.cpp:444:5:444:6 | xs | test.cpp:444:5:444:15 | access to array | +| test.cpp:444:5:444:15 | access to array | test.cpp:444:5:444:19 | Store: ... = ... | +| test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:6 | xs | +| test.cpp:455:5:455:6 | xs | test.cpp:455:5:455:15 | access to array | +| test.cpp:455:5:455:15 | access to array | test.cpp:455:5:455:19 | Store: ... = ... | +| test.cpp:461:14:461:27 | new[] | test.cpp:466:5:466:6 | xs | +| test.cpp:466:5:466:6 | xs | test.cpp:466:5:466:15 | access to array | +| test.cpp:466:5:466:15 | access to array | test.cpp:466:5:466:19 | Store: ... = ... | +| test.cpp:472:14:472:27 | new[] | test.cpp:477:5:477:6 | xs | +| test.cpp:477:5:477:6 | xs | test.cpp:477:5:477:15 | access to array | +| test.cpp:477:5:477:15 | access to array | test.cpp:477:5:477:19 | Store: ... = ... | +| test.cpp:483:14:483:27 | new[] | test.cpp:488:5:488:6 | xs | +| test.cpp:488:5:488:6 | xs | test.cpp:488:5:488:15 | access to array | +| test.cpp:488:5:488:15 | access to array | test.cpp:488:5:488:19 | Store: ... = ... | +| test.cpp:494:14:494:31 | new[] | test.cpp:499:5:499:6 | xs | +| test.cpp:505:14:505:31 | new[] | test.cpp:510:5:510:6 | xs | +| test.cpp:516:14:516:31 | new[] | test.cpp:521:5:521:6 | xs | +| test.cpp:527:14:527:31 | new[] | test.cpp:532:5:532:6 | xs | +| test.cpp:538:14:538:31 | new[] | test.cpp:543:5:543:6 | xs | +| test.cpp:549:14:549:31 | new[] | test.cpp:554:5:554:6 | xs | +| test.cpp:554:5:554:6 | xs | test.cpp:554:5:554:15 | access to array | +| test.cpp:554:5:554:15 | access to array | test.cpp:554:5:554:19 | Store: ... = ... | 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 | @@ -1157,6 +1180,40 @@ nodes | test.cpp:433:5:433:6 | xs | semmle.label | xs | | test.cpp:433:5:433:17 | access to array | semmle.label | access to array | | test.cpp:433:5:433:21 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:439:14:439:27 | new[] | semmle.label | new[] | +| test.cpp:444:5:444:6 | xs | semmle.label | xs | +| test.cpp:444:5:444:15 | access to array | semmle.label | access to array | +| test.cpp:444:5:444:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:450:14:450:27 | new[] | semmle.label | new[] | +| test.cpp:455:5:455:6 | xs | semmle.label | xs | +| test.cpp:455:5:455:15 | access to array | semmle.label | access to array | +| test.cpp:455:5:455:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:461:14:461:27 | new[] | semmle.label | new[] | +| test.cpp:466:5:466:6 | xs | semmle.label | xs | +| test.cpp:466:5:466:15 | access to array | semmle.label | access to array | +| test.cpp:466:5:466:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:472:14:472:27 | new[] | semmle.label | new[] | +| test.cpp:477:5:477:6 | xs | semmle.label | xs | +| test.cpp:477:5:477:15 | access to array | semmle.label | access to array | +| test.cpp:477:5:477:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:483:14:483:27 | new[] | semmle.label | new[] | +| test.cpp:488:5:488:6 | xs | semmle.label | xs | +| test.cpp:488:5:488:15 | access to array | semmle.label | access to array | +| test.cpp:488:5:488:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:494:14:494:31 | new[] | semmle.label | new[] | +| test.cpp:499:5:499:6 | xs | semmle.label | xs | +| test.cpp:505:14:505:31 | new[] | semmle.label | new[] | +| test.cpp:510:5:510:6 | xs | semmle.label | xs | +| test.cpp:516:14:516:31 | new[] | semmle.label | new[] | +| test.cpp:521:5:521:6 | xs | semmle.label | xs | +| test.cpp:527:14:527:31 | new[] | semmle.label | new[] | +| test.cpp:532:5:532:6 | xs | semmle.label | xs | +| test.cpp:538:14:538:31 | new[] | semmle.label | new[] | +| test.cpp:543:5:543:6 | xs | semmle.label | xs | +| test.cpp:549:14:549:31 | new[] | semmle.label | new[] | +| test.cpp:554:5:554:6 | xs | semmle.label | xs | +| test.cpp:554:5:554:15 | access to array | semmle.label | access to array | +| test.cpp:554:5:554:19 | Store: ... = ... | semmle.label | Store: ... = ... | 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 | @@ -1187,3 +1244,9 @@ subpaths | test.cpp:407:3:407:22 | Store: ... = ... | test.cpp:404:12:404:25 | new[] | test.cpp:407:3:407:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:404:12:404:25 | new[] | new[] | test.cpp:407:10:407:17 | ... - ... | ... - ... | | test.cpp:419:7:419:15 | Store: ... = ... | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:417:16:417:33 | new[] | new[] | test.cpp:419:10:419:10 | i | i | | test.cpp:433:5:433:21 | Store: ... = ... | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:427:14:427:27 | new[] | new[] | test.cpp:433:8:433:16 | ... ++ | ... ++ | +| test.cpp:444:5:444:19 | Store: ... = ... | test.cpp:439:14:439:27 | new[] | test.cpp:444:5:444:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:439:14:439:27 | new[] | new[] | test.cpp:444:8:444:14 | src_pos | src_pos | +| test.cpp:455:5:455:19 | Store: ... = ... | test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:450:14:450:27 | new[] | new[] | test.cpp:455:8:455:14 | src_pos | src_pos | +| test.cpp:466:5:466:19 | Store: ... = ... | test.cpp:461:14:461:27 | new[] | test.cpp:466:5:466:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:461:14:461:27 | new[] | new[] | test.cpp:466:8:466:14 | src_pos | src_pos | +| test.cpp:477:5:477:19 | Store: ... = ... | test.cpp:472:14:472:27 | new[] | test.cpp:477:5:477:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:472:14:472:27 | new[] | new[] | test.cpp:477:8:477:14 | src_pos | src_pos | +| test.cpp:488:5:488:19 | Store: ... = ... | test.cpp:483:14:483:27 | new[] | test.cpp:488:5:488:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:483:14:483:27 | new[] | new[] | test.cpp:488:8:488:14 | src_pos | src_pos | +| test.cpp:554:5:554:19 | Store: ... = ... | test.cpp:549:14:549:31 | new[] | test.cpp:554:5:554:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:549:14:549:31 | new[] | new[] | test.cpp:554:8:554:14 | src_pos | src_pos | 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 4048c15be8b..3fd047fa180 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 @@ -433,3 +433,124 @@ void test31(unsigned size, unsigned src_pos) xs[dst_pos++] = 0; // GOOD [FALSE POSITIVE] } } + +void test31_simple1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size) { + xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + } +} + +void test31_simple2(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size + 1) { + xs[src_pos] = 0; // BAD + } +} + +void test31_simple3(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos - 1 < size) { + xs[src_pos] = 0; // BAD + } +} + +void test31_simple4(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size - 1) { + xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + } +} + +void test31_simple5(unsigned size, unsigned src_pos) +{ + char *xs = new char[size]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos + 1 < size) { + xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + } +} + +void test31_simple1_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple2_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size + 1) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple3_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos - 1 < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple4_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size - 1) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple5_plus1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos + 1 < size) { + xs[src_pos] = 0; // GOOD + } +} + +void test31_simple1_sub1(unsigned size, unsigned src_pos) +{ + char *xs = new char[size - 1]; + if (src_pos > size) { + src_pos = size; + } + if(src_pos < size) { + xs[src_pos] = 0; // BAD + } +} From 9d5b8cff2eee94e7afc79e3dd6a31911841b94ac Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Sat, 24 Jun 2023 19:00:08 +0100 Subject: [PATCH 23/93] C++: Add a barrier to the 'cpp/invalid-pointer-deref' query. --- .../CWE/CWE-193/InvalidPointerDeref.ql | 111 ++++++++++++++++-- 1 file changed, 100 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 478ab2cc92a..a202f3ed380 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -19,6 +19,8 @@ import cpp import semmle.code.cpp.ir.dataflow.internal.ProductFlow import semmle.code.cpp.rangeanalysis.new.internal.semantic.analysis.RangeAnalysis import semmle.code.cpp.rangeanalysis.new.internal.semantic.SemanticExprSpecific +import semmle.code.cpp.ir.ValueNumbering +import semmle.code.cpp.controlflow.IRGuards import semmle.code.cpp.ir.IR import codeql.util.Unit @@ -67,6 +69,84 @@ predicate hasSize(HeuristicAllocationExpr alloc, DataFlow::Node n, int state) { ) } +/** + * A module that encapsulates a barrier guard to remove false positives from flow like: + * ```cpp + * char *p = new char[size]; + * // ... + * unsigned n = size; + * // ... + * if(n < size) { + * use(*p[n]); + * } + * ``` + * In this case, the sink pair identified by the product flow library (without any additional barriers) + * would be `(p, n)` (where `n` is the `n` in `p[n]`), because there exists a pointer-arithmetic + * instruction `pai` such that: + * 1. The left-hand of `pai` flows from the allocation, and + * 2. The right-hand of `pai` is non-strictly upper bounded by `n` (where `n` is the `n` in `p[n]`) + * but because there's a strict comparison that compares `n` against the size of the allocation this + * snippet is fine. + */ +module Barrier2 { + private class FlowState2 = AllocToInvalidPointerConfig::FlowState2; + + private module BarrierConfig2 implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for the second + // projection in the `AllocToInvalidPointerConfig` module. + hasSize(_, source, _) + } + + additional predicate isSink( + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, FlowState2 state, + boolean testIsTrue + ) { + // The sink is any "large" side of a relational comparison. + g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + } + + predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } + } + + private import DataFlow::Global + + private FlowState2 getAFlowStateForNode(DataFlow::Node node) { + exists(DataFlow::Node source | + flow(source, node) and + hasSize(_, source, result) + ) + } + + private predicate operandGuardChecks( + IRGuardCondition g, Operand left, Operand right, FlowState2 state, boolean edge + ) { + exists(DataFlow::Node nLeft, DataFlow::Node nRight, FlowState2 state0 | + nRight.asOperand() = right and + nLeft.asOperand() = left and + BarrierConfig2::isSink(nLeft, nRight, g, state0, edge) and + state = getAFlowStateForNode(nRight) and + state0 <= state + ) + } + + Instruction getABarrierInstruction(FlowState2 state) { + exists(IRGuardCondition g, Operand right, ValueNumber value, boolean edge | + operandGuardChecks(g, value.getAUse(), right, state, edge) and + result = value.getAnInstruction() and + g.controls(result.getBlock(), edge) + ) + } + + DataFlow::Node getABarrierNode(FlowState2 state) { + result.asOperand() = getABarrierInstruction(state).getAUse() + } + + IRBlock getABarrierBlock(FlowState2 state) { + result.getAnInstruction() = getABarrierInstruction(state) + } +} + /** * A product-flow configuration for flow from an (allocation, size) pair to a * pointer-arithmetic operation that is non-strictly upper-bounded by `allocation + size`. @@ -111,15 +191,14 @@ module AllocToInvalidPointerConfig implements ProductFlow::StateConfigSig { exists(state1) and // We check that the delta computed by the range analysis matches the // state value that we set in `isSourcePair`. - exists(int delta | - isSinkImpl(_, sink1, sink2, delta) and - state2 = delta - ) + isSinkImpl(_, sink1, sink2, state2) } predicate isBarrier1(DataFlow::Node node, FlowState1 state) { none() } - predicate isBarrier2(DataFlow::Node node, FlowState2 state) { none() } + predicate isBarrier2(DataFlow::Node node, FlowState2 state) { + node = Barrier2::getABarrierNode(state) + } predicate isBarrierIn1(DataFlow::Node node) { isSourcePair(node, _, _, _) } @@ -163,7 +242,8 @@ predicate pointerAddInstructionHasBounds( exists(Instruction right | pai.getRight() = right and pai.getLeft() = sink1.asInstruction() and - bounded1(right, sink2.asInstruction(), delta) + bounded1(right, sink2.asInstruction(), delta) and + not [right, sink2.asInstruction()] = Barrier2::getABarrierInstruction(delta) ) } @@ -246,12 +326,21 @@ module InvalidPointerToDerefFlow = DataFlow::Global predicate invalidPointerToDerefSource( PointerArithmeticInstruction pai, DataFlow::Node source, int delta ) { - exists(AllocToInvalidPointerFlow::PathNode1 p, DataFlow::Node sink1 | - pragma[only_bind_out](p.getNode()) = sink1 and - AllocToInvalidPointerFlow::flowPath(_, _, pragma[only_bind_into](p), _) and - isSinkImpl(pai, sink1, _, _) and + exists( + AllocToInvalidPointerFlow::PathNode1 p1, AllocToInvalidPointerFlow::PathNode2 p2, + DataFlow::Node sink1, DataFlow::Node sink2, int delta0 + | + pragma[only_bind_out](p1.getNode()) = sink1 and + pragma[only_bind_out](p2.getNode()) = sink2 and + AllocToInvalidPointerFlow::flowPath(_, _, pragma[only_bind_into](p1), pragma[only_bind_into](p2)) and + // Note that `delta` is not necessarily equal to `delta0`: + // `delta0` is the constant offset added to the size of the allocation, and + // delta is the constant difference between the pointer-arithmetic instruction + // and the instruction computing the address for which we will search for a dereference. + isSinkImpl(pai, sink1, sink2, delta0) and bounded2(source.asInstruction(), pai, delta) and - delta >= 0 + delta >= 0 and + not source.getBasicBlock() = Barrier2::getABarrierBlock(delta0) ) } From c1077fe75d4819fe6a3f64c811d155344cf5a334 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Sat, 24 Jun 2023 19:01:23 +0100 Subject: [PATCH 24/93] C++: Accept test changes. --- .../InvalidPointerDeref.expected | 25 ------------------- .../CWE/CWE-193/pointer-deref/test.cpp | 10 ++++---- 2 files changed, 5 insertions(+), 30 deletions(-) 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 121edbaa801..8d0233216b6 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 @@ -783,14 +783,8 @@ edges | test.cpp:407:7:407:8 | xs | test.cpp:407:3:407:18 | access to array | | test.cpp:407:7:407:8 | xs indirection | test.cpp:407:7:407:8 | xs | | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:8 | xs | -| test.cpp:419:7:419:8 | xs | test.cpp:419:7:419:11 | access to array | -| test.cpp:419:7:419:11 | access to array | test.cpp:419:7:419:15 | Store: ... = ... | | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:6 | xs | -| test.cpp:433:5:433:6 | xs | test.cpp:433:5:433:17 | access to array | -| test.cpp:433:5:433:17 | access to array | test.cpp:433:5:433:21 | Store: ... = ... | | test.cpp:439:14:439:27 | new[] | test.cpp:444:5:444:6 | xs | -| test.cpp:444:5:444:6 | xs | test.cpp:444:5:444:15 | access to array | -| test.cpp:444:5:444:15 | access to array | test.cpp:444:5:444:19 | Store: ... = ... | | test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:6 | xs | | test.cpp:455:5:455:6 | xs | test.cpp:455:5:455:15 | access to array | | test.cpp:455:5:455:15 | access to array | test.cpp:455:5:455:19 | Store: ... = ... | @@ -798,11 +792,7 @@ edges | test.cpp:466:5:466:6 | xs | test.cpp:466:5:466:15 | access to array | | test.cpp:466:5:466:15 | access to array | test.cpp:466:5:466:19 | Store: ... = ... | | test.cpp:472:14:472:27 | new[] | test.cpp:477:5:477:6 | xs | -| test.cpp:477:5:477:6 | xs | test.cpp:477:5:477:15 | access to array | -| test.cpp:477:5:477:15 | access to array | test.cpp:477:5:477:19 | Store: ... = ... | | test.cpp:483:14:483:27 | new[] | test.cpp:488:5:488:6 | xs | -| test.cpp:488:5:488:6 | xs | test.cpp:488:5:488:15 | access to array | -| test.cpp:488:5:488:15 | access to array | test.cpp:488:5:488:19 | Store: ... = ... | | test.cpp:494:14:494:31 | new[] | test.cpp:499:5:499:6 | xs | | test.cpp:505:14:505:31 | new[] | test.cpp:510:5:510:6 | xs | | test.cpp:516:14:516:31 | new[] | test.cpp:521:5:521:6 | xs | @@ -1174,16 +1164,10 @@ nodes | test.cpp:407:7:407:8 | xs indirection | semmle.label | xs indirection | | test.cpp:417:16:417:33 | new[] | semmle.label | new[] | | test.cpp:419:7:419:8 | xs | semmle.label | xs | -| test.cpp:419:7:419:11 | access to array | semmle.label | access to array | -| test.cpp:419:7:419:15 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:427:14:427:27 | new[] | semmle.label | new[] | | test.cpp:433:5:433:6 | xs | semmle.label | xs | -| test.cpp:433:5:433:17 | access to array | semmle.label | access to array | -| test.cpp:433:5:433:21 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:439:14:439:27 | new[] | semmle.label | new[] | | test.cpp:444:5:444:6 | xs | semmle.label | xs | -| test.cpp:444:5:444:15 | access to array | semmle.label | access to array | -| test.cpp:444:5:444:19 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:450:14:450:27 | new[] | semmle.label | new[] | | test.cpp:455:5:455:6 | xs | semmle.label | xs | | test.cpp:455:5:455:15 | access to array | semmle.label | access to array | @@ -1194,12 +1178,8 @@ nodes | test.cpp:466:5:466:19 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:472:14:472:27 | new[] | semmle.label | new[] | | test.cpp:477:5:477:6 | xs | semmle.label | xs | -| test.cpp:477:5:477:15 | access to array | semmle.label | access to array | -| test.cpp:477:5:477:19 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:483:14:483:27 | new[] | semmle.label | new[] | | test.cpp:488:5:488:6 | xs | semmle.label | xs | -| test.cpp:488:5:488:15 | access to array | semmle.label | access to array | -| test.cpp:488:5:488:19 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:494:14:494:31 | new[] | semmle.label | new[] | | test.cpp:499:5:499:6 | xs | semmle.label | xs | | test.cpp:505:14:505:31 | new[] | semmle.label | new[] | @@ -1242,11 +1222,6 @@ subpaths | test.cpp:384:13:384:16 | Load: * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size | | test.cpp:395:5:395:13 | Store: ... = ... | test.cpp:388:14:388:27 | new[] | test.cpp:395:5:395:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:388:14:388:27 | new[] | new[] | test.cpp:389:19:389:22 | size | size | | test.cpp:407:3:407:22 | Store: ... = ... | test.cpp:404:12:404:25 | new[] | test.cpp:407:3:407:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:404:12:404:25 | new[] | new[] | test.cpp:407:10:407:17 | ... - ... | ... - ... | -| test.cpp:419:7:419:15 | Store: ... = ... | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:417:16:417:33 | new[] | new[] | test.cpp:419:10:419:10 | i | i | -| test.cpp:433:5:433:21 | Store: ... = ... | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:427:14:427:27 | new[] | new[] | test.cpp:433:8:433:16 | ... ++ | ... ++ | -| test.cpp:444:5:444:19 | Store: ... = ... | test.cpp:439:14:439:27 | new[] | test.cpp:444:5:444:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:439:14:439:27 | new[] | new[] | test.cpp:444:8:444:14 | src_pos | src_pos | | test.cpp:455:5:455:19 | Store: ... = ... | test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:450:14:450:27 | new[] | new[] | test.cpp:455:8:455:14 | src_pos | src_pos | | test.cpp:466:5:466:19 | Store: ... = ... | test.cpp:461:14:461:27 | new[] | test.cpp:466:5:466:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:461:14:461:27 | new[] | new[] | test.cpp:466:8:466:14 | src_pos | src_pos | -| test.cpp:477:5:477:19 | Store: ... = ... | test.cpp:472:14:472:27 | new[] | test.cpp:477:5:477:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:472:14:472:27 | new[] | new[] | test.cpp:477:8:477:14 | src_pos | src_pos | -| test.cpp:488:5:488:19 | Store: ... = ... | test.cpp:483:14:483:27 | new[] | test.cpp:488:5:488:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:483:14:483:27 | new[] | new[] | test.cpp:488:8:488:14 | src_pos | src_pos | | test.cpp:554:5:554:19 | Store: ... = ... | test.cpp:549:14:549:31 | new[] | test.cpp:554:5:554:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:549:14:549:31 | new[] | new[] | test.cpp:554:8:554:14 | src_pos | src_pos | 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 3fd047fa180..1ff924f95a3 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 @@ -416,7 +416,7 @@ void test30(int *size) new_size = tmp_size + 1; char *xs = new char[new_size]; for (int i = 0; i < new_size; i++) { - xs[i] = 0; // GOOD [FALSE POSITIVE] + xs[i] = 0; // GOOD } } *size = new_size; @@ -430,7 +430,7 @@ void test31(unsigned size, unsigned src_pos) } unsigned dst_pos = src_pos; if(dst_pos < size - 3) { - xs[dst_pos++] = 0; // GOOD [FALSE POSITIVE] + xs[dst_pos++] = 0; // GOOD } } @@ -441,7 +441,7 @@ void test31_simple1(unsigned size, unsigned src_pos) src_pos = size; } if(src_pos < size) { - xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + xs[src_pos] = 0; // GOOD } } @@ -474,7 +474,7 @@ void test31_simple4(unsigned size, unsigned src_pos) src_pos = size; } if(src_pos < size - 1) { - xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + xs[src_pos] = 0; // GOOD } } @@ -485,7 +485,7 @@ void test31_simple5(unsigned size, unsigned src_pos) src_pos = size; } if(src_pos + 1 < size) { - xs[src_pos] = 0; // GOOD [FALSE POSITIVE] + xs[src_pos] = 0; // GOOD } } From e0f5c584b976597982a76a86ac9145c41197b824 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Sat, 24 Jun 2023 19:38:22 +0100 Subject: [PATCH 25/93] C++: Fix Code Scanning error. --- .../experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql | 4 ++-- 1 file changed, 2 insertions(+), 2 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 a202f3ed380..4249f3a12b9 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -131,8 +131,8 @@ module Barrier2 { } Instruction getABarrierInstruction(FlowState2 state) { - exists(IRGuardCondition g, Operand right, ValueNumber value, boolean edge | - operandGuardChecks(g, value.getAUse(), right, state, edge) and + exists(IRGuardCondition g, ValueNumber value, boolean edge | + operandGuardChecks(g, value.getAUse(), _, state, edge) and result = value.getAnInstruction() and g.controls(result.getBlock(), edge) ) From 458522a656bf436be5f9ea12233a2cfc13aad98b Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 26 Jun 2023 12:11:03 +0200 Subject: [PATCH 26/93] C++: Implement `clearsContent` for IR dataflow --- .../ir/dataflow/internal/DataFlowPrivate.qll | 70 ++++- .../cpp/ir/dataflow/internal/DataFlowUtil.qll | 46 ++- .../ArrayAccessProductFlow.expected | 14 +- .../InvalidPointerDeref.expected | 27 +- .../CWE/CWE-193/pointer-deref/test.cpp | 2 +- .../dataflow/fields/clearning.cpp | 182 ++++++++++++ .../fields/dataflow-consistency.expected | 29 ++ .../fields/dataflow-ir-consistency.expected | 11 + .../dataflow/fields/ir-path-flow.expected | 270 +++++++++++++++++- .../fields/partial-definition-diff.expected | 60 ++++ .../fields/partial-definition-ir.expected | 84 ++++++ .../fields/partial-definition.expected | 86 ++++++ .../dataflow/fields/path-flow.expected | 90 ++++++ .../SAMATE/OverrunWriteProductFlow.expected | 28 +- 14 files changed, 944 insertions(+), 55 deletions(-) create mode 100644 cpp/ql/test/library-tests/dataflow/fields/clearning.cpp diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 6715b67c382..85a4a224e52 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -651,13 +651,16 @@ predicate jumpStep(Node n1, Node n2) { * Holds if data can flow from `node1` to `node2` via an assignment to `f`. * Thus, `node2` references an object with a field `f` that contains the * value of `node1`. + * + * The boolean `certain` is true if the destination address does not involve + * any pointer arithmetic, and false otherwise. */ -predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { +predicate storeStepImpl(Node node1, Content c, PostFieldUpdateNode node2, boolean certain) { exists(int indirectionIndex1, int numberOfLoads, StoreInstruction store | nodeHasInstruction(node1, store, pragma[only_bind_into](indirectionIndex1)) and node2.getIndirectionIndex() = 1 and numberOfLoadsFromOperand(node2.getFieldAddress(), store.getDestinationAddressOperand(), - numberOfLoads) + numberOfLoads, certain) | exists(FieldContent fc | fc = c | fc.getField() = node2.getUpdatedField() and @@ -671,21 +674,34 @@ predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { ) } +/** + * Holds if data can flow from `node1` to `node2` via an assignment to `f`. + * Thus, `node2` references an object with a field `f` that contains the + * value of `node1`. + */ +predicate storeStep(Node node1, Content c, PostFieldUpdateNode node2) { + storeStepImpl(node1, c, node2, _) +} + /** * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * operations and exactly `n` `LoadInstruction` operations. */ -private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand operandTo, int ind) { +private predicate numberOfLoadsFromOperandRec( + Operand operandFrom, Operand operandTo, int ind, boolean certain +) { exists(Instruction load | Ssa::isDereference(load, operandFrom) | - operandTo = operandFrom and ind = 0 + operandTo = operandFrom and ind = 0 and certain = true or - numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1) + numberOfLoadsFromOperand(load.getAUse(), operandTo, ind - 1, certain) ) or - exists(Operand op, Instruction instr | + exists(Operand op, Instruction instr, boolean isPointerArith, boolean certain0 | instr = op.getDef() and - conversionFlow(operandFrom, instr, _, _) and - numberOfLoadsFromOperand(op, operandTo, ind) + conversionFlow(operandFrom, instr, isPointerArith, _) and + numberOfLoadsFromOperand(op, operandTo, ind, certain0) + | + if isPointerArith = true then certain = false else certain = certain0 ) } @@ -693,13 +709,16 @@ private predicate numberOfLoadsFromOperandRec(Operand operandFrom, Operand opera * Holds if `operandFrom` flows to `operandTo` using a sequence of conversion-like * operations and exactly `n` `LoadInstruction` operations. */ -private predicate numberOfLoadsFromOperand(Operand operandFrom, Operand operandTo, int n) { - numberOfLoadsFromOperandRec(operandFrom, operandTo, n) +private predicate numberOfLoadsFromOperand( + Operand operandFrom, Operand operandTo, int n, boolean certain +) { + numberOfLoadsFromOperandRec(operandFrom, operandTo, n, certain) or not Ssa::isDereference(_, operandFrom) and not conversionFlow(operandFrom, _, _, _) and operandFrom = operandTo and - n = 0 + n = 0 and + certain = true } // Needed to join on both an operand and an index at the same time. @@ -729,7 +748,7 @@ predicate readStep(Node node1, Content c, Node node2) { // The `1` here matches the `node2.getIndirectionIndex() = 1` conjunct // in `storeStep`. nodeHasOperand(node1, fa1.getObjectAddressOperand(), 1) and - numberOfLoadsFromOperand(fa1, operand, numberOfLoads) + numberOfLoadsFromOperand(fa1, operand, numberOfLoads, _) | exists(FieldContent fc | fc = c | fc.getField() = fa1.getField() and @@ -747,7 +766,32 @@ predicate readStep(Node node1, Content c, Node node2) { * Holds if values stored inside content `c` are cleared at node `n`. */ predicate clearsContent(Node n, Content c) { - none() // stub implementation + n = + any(PostUpdateNode pun, Content d | d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) | pun) + .getPreUpdateNode() and + ( + // The crement operations and pointer addition and subtraction self-assign. We do not + // want to clear the contents if it is indirectly pointed at by any of these operations, + // as part of the contents might still be accessible afterwards. If there is no such + // indirection clearing the contents is safe. + not exists(Operand op, Cpp::Operation p | + n.(IndirectOperand).hasOperandAndIndirectionIndex(op, _) and + ( + p instanceof Cpp::AssignPointerAddExpr or + p instanceof Cpp::AssignPointerSubExpr or + p instanceof Cpp::CrementOperation + ) + | + p.getAnOperand() = op.getUse().getAst() + ) + or + not exists(PostUpdateNode pun, Content d | + d.impliesClearOf(c) and + storeStepImpl(_, d, pun, true) and + c.getIndirectionIndex() > d.getIndirectionIndex() and + pun.getPreUpdateNode() = n + ) + ) } /** diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 4cf5cd65fa8..11a4b817dbf 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -1832,6 +1832,26 @@ class Content extends TContent { predicate hasLocationInfo(string path, int sl, int sc, int el, int ec) { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } + + /** + * INTERNAL: Do not use. + * + * Holds if a write to this `Content` implies that `c` is + * also cleared. + * + * For example, a write to a field `f` implies that any content of + * the form `*f` is also cleared. + */ + abstract predicate impliesClearOf(Content c); + + abstract int getIndirectionIndex(); +} + +predicate foo(FieldContent f) { + exists(int i, Field ff | + i = f.getIndirectionIndex() and + ff = f.getField() + ) } /** A reference through a non-union instance field. */ @@ -1850,9 +1870,19 @@ class FieldContent extends Content, TFieldContent { Field getField() { result = f } pragma[inline] - int getIndirectionIndex() { + override int getIndirectionIndex() { pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) } + + override predicate impliesClearOf(Content c) { + exists(FieldContent fc | + fc = c and + fc.getField() = f and + // If `this` is `f` then `c` is cleared if it's of the + // form `*f`, `**f`, etc. + fc.getIndirectionIndex() >= indirectionIndex + ) + } } /** A reference through an instance field of a union. */ @@ -1877,9 +1907,21 @@ class UnionContent extends Content, TUnionContent { /** Gets the indirection index of this `UnionContent`. */ pragma[inline] - int getIndirectionIndex() { + override int getIndirectionIndex() { pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) } + + override predicate impliesClearOf(Content c) { + exists(UnionContent uc | + uc = c and + uc.getUnion() = u and + // If `this` is `u` then `c` is cleared if it's of the + // form `*u`, `**u`, etc. (and we ignore `bytes` because + // we know the entire union is overwritten because it's a + // union). + uc.getIndirectionIndex() >= indirectionIndex + ) + } } /** diff --git a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected index 820c48447ff..dbd71611d81 100644 --- a/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected +++ b/cpp/ql/test/experimental/query-tests/Security/CWE/CWE-193/array-access/ArrayAccessProductFlow.expected @@ -4,8 +4,9 @@ edges | test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:28:19:28:26 | call to mk_array [p] | | test.cpp:19:9:19:16 | mk_array indirection [p] | test.cpp:50:18:50:25 | call to mk_array [p] | | test.cpp:21:5:21:24 | ... = ... | test.cpp:21:9:21:9 | arr indirection [post update] [p] | -| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:19:9:19:16 | mk_array indirection [p] | +| test.cpp:21:9:21:9 | arr indirection [post update] [p] | test.cpp:22:5:22:7 | arr indirection [p] | | test.cpp:21:13:21:18 | call to malloc | test.cpp:21:5:21:24 | ... = ... | +| test.cpp:22:5:22:7 | arr indirection [p] | test.cpp:19:9:19:16 | mk_array indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:31:9:31:11 | arr indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | test.cpp:35:9:35:11 | arr indirection [p] | | test.cpp:31:9:31:11 | arr indirection [p] | test.cpp:31:13:31:13 | p indirection | @@ -20,9 +21,10 @@ edges | test.cpp:45:13:45:13 | p indirection | test.cpp:45:13:45:13 | p | | test.cpp:50:18:50:25 | call to mk_array [p] | test.cpp:39:27:39:29 | arr [p] | | test.cpp:55:5:55:24 | ... = ... | test.cpp:55:9:55:9 | arr indirection [post update] [p] | -| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:59:9:59:11 | arr indirection [p] | -| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:63:9:63:11 | arr indirection [p] | +| test.cpp:55:9:55:9 | arr indirection [post update] [p] | test.cpp:56:5:56:7 | arr indirection [p] | | test.cpp:55:13:55:18 | call to malloc | test.cpp:55:5:55:24 | ... = ... | +| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:59:9:59:11 | arr indirection [p] | +| test.cpp:56:5:56:7 | arr indirection [p] | test.cpp:63:9:63:11 | arr indirection [p] | | test.cpp:59:9:59:11 | arr indirection [p] | test.cpp:59:13:59:13 | p indirection | | test.cpp:59:13:59:13 | p indirection | test.cpp:59:13:59:13 | p | | test.cpp:63:9:63:11 | arr indirection [p] | test.cpp:63:13:63:13 | p indirection | @@ -30,8 +32,9 @@ edges | test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | | test.cpp:67:10:67:19 | mk_array_p indirection [p] | test.cpp:98:18:98:27 | call to mk_array_p indirection [p] | | test.cpp:69:5:69:25 | ... = ... | test.cpp:69:10:69:10 | arr indirection [post update] [p] | -| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] | +| test.cpp:69:10:69:10 | arr indirection [post update] [p] | test.cpp:70:5:70:7 | arr indirection [p] | | test.cpp:69:14:69:19 | call to malloc | test.cpp:69:5:69:25 | ... = ... | +| test.cpp:70:5:70:7 | arr indirection [p] | test.cpp:67:10:67:19 | mk_array_p indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:79:9:79:11 | arr indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | test.cpp:83:9:83:11 | arr indirection [p] | | test.cpp:79:9:79:11 | arr indirection [p] | test.cpp:79:14:79:14 | p indirection | @@ -53,6 +56,7 @@ nodes | test.cpp:21:5:21:24 | ... = ... | semmle.label | ... = ... | | test.cpp:21:9:21:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:21:13:21:18 | call to malloc | semmle.label | call to malloc | +| test.cpp:22:5:22:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:28:19:28:26 | call to mk_array [p] | semmle.label | call to mk_array [p] | | test.cpp:31:9:31:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:31:13:31:13 | p | semmle.label | p | @@ -71,6 +75,7 @@ nodes | test.cpp:55:5:55:24 | ... = ... | semmle.label | ... = ... | | test.cpp:55:9:55:9 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:55:13:55:18 | call to malloc | semmle.label | call to malloc | +| test.cpp:56:5:56:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:59:9:59:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:59:13:59:13 | p | semmle.label | p | | test.cpp:59:13:59:13 | p indirection | semmle.label | p indirection | @@ -81,6 +86,7 @@ nodes | test.cpp:69:5:69:25 | ... = ... | semmle.label | ... = ... | | test.cpp:69:10:69:10 | arr indirection [post update] [p] | semmle.label | arr indirection [post update] [p] | | test.cpp:69:14:69:19 | call to malloc | semmle.label | call to malloc | +| test.cpp:70:5:70:7 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:76:20:76:29 | call to mk_array_p indirection [p] | semmle.label | call to mk_array_p indirection [p] | | test.cpp:79:9:79:11 | arr indirection [p] | semmle.label | arr indirection [p] | | test.cpp:79:14:79:14 | p | semmle.label | p | 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 7c27659de1f..d23f1d16130 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 @@ -380,9 +380,10 @@ edges | test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:89:19:89:26 | call to mk_array [end] | | test.cpp:80:9:80:16 | mk_array indirection [end] | test.cpp:119:18:119:25 | call to mk_array [end] | | test.cpp:82:5:82:28 | ... = ... | test.cpp:82:9:82:13 | arr indirection [post update] [begin] | -| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] | +| test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:5:83:7 | arr indirection [begin] | | test.cpp:82:9:82:13 | arr indirection [post update] [begin] | test.cpp:83:15:83:17 | arr indirection [begin] | | test.cpp:82:17:82:22 | call to malloc | test.cpp:82:5:82:28 | ... = ... | +| test.cpp:83:5:83:7 | arr indirection [begin] | test.cpp:80:9:80:16 | mk_array indirection [begin] | | test.cpp:83:5:83:30 | ... = ... | test.cpp:83:9:83:11 | arr indirection [post update] [end] | | test.cpp:83:9:83:11 | arr indirection [post update] [end] | test.cpp:80:9:80:16 | mk_array indirection [end] | | test.cpp:83:15:83:17 | arr indirection [begin] | test.cpp:83:19:83:23 | begin indirection | @@ -455,9 +456,10 @@ edges | test.cpp:124:15:124:20 | call to malloc | test.cpp:125:5:125:17 | ... = ... | | test.cpp:124:15:124:20 | call to malloc | test.cpp:126:15:126:15 | p | | test.cpp:125:5:125:17 | ... = ... | test.cpp:125:9:125:13 | arr indirection [post update] [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:129:11:129:13 | arr indirection [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:133:11:133:13 | arr indirection [begin] | -| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:137:11:137:13 | arr indirection [begin] | +| test.cpp:125:9:125:13 | arr indirection [post update] [begin] | test.cpp:126:5:126:7 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:129:11:129:13 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:133:11:133:13 | arr indirection [begin] | +| test.cpp:126:5:126:7 | arr indirection [begin] | test.cpp:137:11:137:13 | arr indirection [begin] | | test.cpp:129:11:129:13 | arr indirection [begin] | test.cpp:129:15:129:19 | begin indirection | | test.cpp:129:15:129:19 | begin indirection | test.cpp:129:15:129:19 | begin | | test.cpp:133:11:133:13 | arr indirection [begin] | test.cpp:133:15:133:19 | begin indirection | @@ -469,9 +471,10 @@ edges | test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:150:20:150:29 | call to mk_array_p indirection [end] | | test.cpp:141:10:141:19 | mk_array_p indirection [end] | test.cpp:180:19:180:28 | call to mk_array_p indirection [end] | | test.cpp:143:5:143:29 | ... = ... | test.cpp:143:10:143:14 | arr indirection [post update] [begin] | -| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] | +| test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:5:144:7 | arr indirection [begin] | | test.cpp:143:10:143:14 | arr indirection [post update] [begin] | test.cpp:144:16:144:18 | arr indirection [begin] | | test.cpp:143:18:143:23 | call to malloc | test.cpp:143:5:143:29 | ... = ... | +| test.cpp:144:5:144:7 | arr indirection [begin] | test.cpp:141:10:141:19 | mk_array_p indirection [begin] | | test.cpp:144:5:144:32 | ... = ... | test.cpp:144:10:144:12 | arr indirection [post update] [end] | | test.cpp:144:10:144:12 | arr indirection [post update] [end] | test.cpp:141:10:141:19 | mk_array_p indirection [end] | | test.cpp:144:16:144:18 | arr indirection [begin] | test.cpp:144:21:144:25 | begin indirection | @@ -772,15 +775,10 @@ edges | test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | | test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | | test.cpp:395:5:395:6 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:404:3:404:25 | ... = ... | test.cpp:404:7:404:8 | val indirection [post update] [xs] | -| test.cpp:404:7:404:8 | val indirection [post update] [xs] | test.cpp:407:3:407:5 | val indirection [xs] | -| test.cpp:404:12:404:25 | new[] | test.cpp:404:3:404:25 | ... = ... | | test.cpp:406:3:406:25 | ... = ... | test.cpp:406:7:406:8 | val indirection [post update] [xs] | | test.cpp:406:7:406:8 | val indirection [post update] [xs] | test.cpp:407:3:407:5 | val indirection [xs] | | test.cpp:406:12:406:25 | new[] | test.cpp:406:3:406:25 | ... = ... | | test.cpp:407:3:407:5 | val indirection [xs] | test.cpp:407:7:407:8 | xs indirection | -| test.cpp:407:3:407:18 | access to array | test.cpp:407:3:407:22 | Store: ... = ... | -| test.cpp:407:7:407:8 | xs | test.cpp:407:3:407:18 | access to array | | test.cpp:407:7:407:8 | xs indirection | test.cpp:407:7:407:8 | xs | | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:8 | xs | | test.cpp:419:7:419:8 | xs | test.cpp:419:7:419:11 | access to array | @@ -880,6 +878,7 @@ nodes | 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:7 | arr indirection [begin] | semmle.label | arr indirection [begin] | | 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] | @@ -939,6 +938,7 @@ nodes | 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:5:126:7 | arr indirection [begin] | semmle.label | arr indirection [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 | @@ -954,6 +954,7 @@ nodes | 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:7 | arr indirection [begin] | semmle.label | arr indirection [begin] | | 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] | @@ -1138,15 +1139,10 @@ nodes | test.cpp:393:9:393:10 | xs | semmle.label | xs | | test.cpp:395:5:395:6 | xs | semmle.label | xs | | test.cpp:395:5:395:13 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:404:3:404:25 | ... = ... | semmle.label | ... = ... | -| test.cpp:404:7:404:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | -| test.cpp:404:12:404:25 | new[] | semmle.label | new[] | | test.cpp:406:3:406:25 | ... = ... | semmle.label | ... = ... | | test.cpp:406:7:406:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | | test.cpp:406:12:406:25 | new[] | semmle.label | new[] | | test.cpp:407:3:407:5 | val indirection [xs] | semmle.label | val indirection [xs] | -| test.cpp:407:3:407:18 | access to array | semmle.label | access to array | -| test.cpp:407:3:407:22 | Store: ... = ... | semmle.label | Store: ... = ... | | test.cpp:407:7:407:8 | xs | semmle.label | xs | | test.cpp:407:7:407:8 | xs indirection | semmle.label | xs indirection | | test.cpp:417:16:417:33 | new[] | semmle.label | new[] | @@ -1184,6 +1180,5 @@ subpaths | 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 | | test.cpp:384:13:384:16 | Load: * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size | | test.cpp:395:5:395:13 | Store: ... = ... | test.cpp:388:14:388:27 | new[] | test.cpp:395:5:395:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:388:14:388:27 | new[] | new[] | test.cpp:389:19:389:22 | size | size | -| test.cpp:407:3:407:22 | Store: ... = ... | test.cpp:404:12:404:25 | new[] | test.cpp:407:3:407:22 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:404:12:404:25 | new[] | new[] | test.cpp:407:10:407:17 | ... - ... | ... - ... | | test.cpp:419:7:419:15 | Store: ... = ... | test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:417:16:417:33 | new[] | new[] | test.cpp:419:10:419:10 | i | i | | test.cpp:433:5:433:21 | Store: ... = ... | test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:21 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:427:14:427:27 | new[] | new[] | test.cpp:433:8:433:16 | ... ++ | ... ++ | 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 4048c15be8b..01538c59efd 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 @@ -404,7 +404,7 @@ void test29(unsigned size) { val.xs = new char[size]; size++; val.xs = new char[size]; - val.xs[size - 1] = 0; // GOOD [FALSE POSITIVE] + val.xs[size - 1] = 0; // GOOD } void test30(int *size) diff --git a/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp new file mode 100644 index 00000000000..cdcc0c7afac --- /dev/null +++ b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp @@ -0,0 +1,182 @@ +// We want a source of user input that can be both a pointer and a non-pointer. So we +// hack the testing a bit by providing an overload that takes a boolean to distinguish +// between the two while still satisfying the test requirement that the function must +// be named `user_input`. +int user_input(); +int* user_input(bool); +void sink(...); +void argument_source(int*); + +struct S { + int** x; +}; + +void test() +{ + { + S s; + **s.x = user_input(); + *s.x = 0; + sink(**s.x); // $ clean, as *s.x was overwritten and that contains the tainted **s.x + } + + { + S s; + **s.x = user_input(); + **s.x = 0; + sink(**s.x); // $ clean, as **s.x was overwritten and tainted + } + + { + S s; + *s.x = user_input(true); + **s.x = 0; + sink(*s.x); // $ ir // not clean, as **s.x was overwritten and is neither equal nor contains the tainted *s.x + } + + { + S s; + *s.x = user_input(true); + s.x = 0; + sink(*s.x); // clean, as s.x was overwritten and contains the tainted *s.x + } + + { + S s; + **s.x = user_input(); + s.x = 0; + sink(*s.x); // clean, as s.x was overwritten and contains the tainted **s.x + } + + { + S s; + *s.x = user_input(true); + s.x++; + sink(s.x); // $ SPURIOUS: ir ast // Cannot tell the difference with the whole array being tainted + } + + { + S s; + **s.x = user_input(); + s.x++; + sink(s.x); // $ SPURIOUS: ir // Cannot tell the difference with the whole array being tainted + } +} + +struct S2 +{ + int* val; +}; + +void test_uncertain_write_is_not_clear() +{ + S2 s; + argument_source(s.val); + s.val[10] = 0; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and only one is overwitten +} + +void test_indirection_should_not_be_cleared_with_write_1() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val[0] = 0; + s.val = s.val + 1; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted, only one if overwritten, and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_with_write_2() { + S2 s; + argument_source(s.val); // *s.val is tainted + *s.val++ = 0; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted, only one if overwritten, and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_1() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val = s.val + 1; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_2() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val++; + sink(*s.val); // $ ir MISSING: ast // not clean, as all elements of s.val are tainted and the updated pointer still points to tainted elements +} + +void test_indirection_should_not_be_cleared_without_write_3() { + S2 s; + argument_source(s.val); // *s.val is tainted + ++s.val; + sink(*s.val); // $ ir MISSING: ast // not clean as the pointer is only moved to the next tainted element +} + +void test_indirection_should_not_be_cleared_without_write_4() { + S2 s; + argument_source(s.val); // *s.val is tainted + s.val += 1; + sink(*s.val); // $ ir MISSING: ast // not clean as the pointer is only moved to the next tainted element +} + +void test_direct_should_be_cleared() { + S2 s; + s.val = user_input(true); // s.val is tainted + s.val += 1; + sink(s.val); // $ SPURIOUS: ast // clean, as s.val was overwritten and tainted +} + +void test_direct_should_be_cleared_post() { + S2 s; + s.val = user_input(true); // s.val is tainted + s.val++; + sink(s.val); // $ SPURIOUS: ast // clean, as s.val was overwritten and tainted +} + +void test_direct_should_be_cleared_pre() { + S2 s; + s.val = user_input(true); // s.val is tainted + ++s.val; + sink(s.val); // $ SPURIOUS: ast // // clean, as s.x was overwritten and tainted +} + +struct S3 +{ + int val; +}; + +void test_direct() { + { + S3 s; + s.val = user_input(); + sink(s.val); // $ ir ast + } + + { + S3 s; + s.val = user_input(); + s.val = 0; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val++; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val += 1; + sink(s.val); // $ SPURIOUS: ast // clean + } + + { + S3 s; + s.val = user_input(); + s.val = s.val + 1; + sink(s.val); // $ SPURIOUS: ast // clean + } +} diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected index 71c84a0446d..e8f54d3d7ba 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-consistency.expected @@ -43,6 +43,9 @@ argHasPostUpdate | arrays.cpp:10:8:10:15 | * ... | ArgumentNode is missing PostUpdateNode. | | arrays.cpp:16:8:16:13 | access to array | ArgumentNode is missing PostUpdateNode. | | arrays.cpp:17:8:17:13 | access to array | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:34:8:34:11 | * ... | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:41:8:41:11 | * ... | ArgumentNode is missing PostUpdateNode. | +| clearning.cpp:48:8:48:11 | * ... | ArgumentNode is missing PostUpdateNode. | postWithInFlow | A.cpp:25:13:25:13 | c [post update] | PostUpdateNode should not be the target of local flow. | | A.cpp:27:28:27:28 | c [post update] | PostUpdateNode should not be the target of local flow. | @@ -123,6 +126,32 @@ postWithInFlow | by_reference.cpp:108:24:108:24 | a [inner post update] | PostUpdateNode should not be the target of local flow. | | by_reference.cpp:123:28:123:36 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | | by_reference.cpp:127:30:127:38 | inner_ptr [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:19:3:19:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:19:6:19:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:32:3:32:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:32:6:32:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:39:3:39:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:39:6:39:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:40:5:40:5 | x [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:47:5:47:5 | x [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:53:3:53:6 | * ... [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:53:6:53:6 | x [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:75:2:75:10 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:75:4:75:6 | val [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:82:2:82:9 | access to array [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:82:4:82:6 | val [inner post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:83:7:83:9 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:97:4:97:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:124:4:124:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:131:4:131:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:138:4:138:6 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:151:5:151:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:157:5:157:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:158:5:158:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:164:5:164:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:171:5:171:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:178:5:178:7 | val [post update] | PostUpdateNode should not be the target of local flow. | +| clearning.cpp:179:5:179:7 | val [post update] | PostUpdateNode should not be the target of local flow. | | complex.cpp:11:22:11:23 | a_ [post update] | PostUpdateNode should not be the target of local flow. | | complex.cpp:12:22:12:23 | b_ [post update] | PostUpdateNode should not be the target of local flow. | | conflated.cpp:10:3:10:7 | * ... [post update] | PostUpdateNode should not be the target of local flow. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected index ba007019708..b1acebfde5b 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/dataflow-ir-consistency.expected @@ -19,6 +19,17 @@ uniquePostUpdate | aliasing.cpp:77:11:77:11 | definition of w indirection | Node has multiple PostUpdateNodes. | | aliasing.cpp:84:11:84:11 | definition of w indirection | Node has multiple PostUpdateNodes. | | aliasing.cpp:91:11:91:11 | definition of w indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:54:3:54:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:61:3:61:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:90:3:90:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:104:2:104:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:111:4:111:4 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:118:2:118:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:125:2:125:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:132:2:132:2 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:139:4:139:4 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:165:3:165:3 | s indirection | Node has multiple PostUpdateNodes. | +| clearning.cpp:172:3:172:3 | s indirection | Node has multiple PostUpdateNodes. | | complex.cpp:22:3:22:5 | this indirection | Node has multiple PostUpdateNodes. | | complex.cpp:25:7:25:7 | this indirection | Node has multiple PostUpdateNodes. | | complex.cpp:42:10:42:14 | inner indirection | Node has multiple PostUpdateNodes. | diff --git a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected index ec21a37dd3f..a90f04df3cf 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/ir-path-flow.expected @@ -572,6 +572,136 @@ edges | by_reference.cpp:136:8:136:13 | pouter indirection [a] | by_reference.cpp:136:16:136:16 | a | | by_reference.cpp:136:8:136:13 | pouter indirection [a] | by_reference.cpp:136:16:136:16 | a indirection | | by_reference.cpp:136:16:136:16 | a indirection | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:32:3:32:25 | ... = ... | clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | +| clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | clearning.cpp:33:5:33:5 | s indirection [x indirection] | +| clearning.cpp:32:10:32:19 | call to user_input | clearning.cpp:32:3:32:25 | ... = ... | +| clearning.cpp:33:5:33:5 | s indirection [x indirection] | clearning.cpp:34:9:34:9 | s indirection [x indirection] | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:11:34:11 | x indirection | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | clearning.cpp:34:11:34:11 | x indirection | +| clearning.cpp:34:11:34:11 | x indirection | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:11:34:11 | x indirection | clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:53:3:53:25 | ... = ... | clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | +| clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | clearning.cpp:54:3:54:3 | s indirection [x indirection] | +| clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:53:3:53:25 | ... = ... | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:54:5:54:5 | x indirection | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | clearning.cpp:55:8:55:8 | s indirection [x indirection] | +| clearning.cpp:54:3:54:7 | ... ++ indirection | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:54:3:54:7 | ... ++ indirection | clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | +| clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | clearning.cpp:55:8:55:8 | s indirection [x indirection] | +| clearning.cpp:54:5:54:5 | x indirection | clearning.cpp:54:3:54:7 | ... ++ indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:55:10:55:10 | x indirection | clearning.cpp:55:10:55:10 | x indirection | +| clearning.cpp:60:3:60:22 | ... = ... | clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | +| clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | clearning.cpp:61:3:61:3 | s indirection [x indirection] | +| clearning.cpp:60:11:60:20 | call to user_input | clearning.cpp:60:3:60:22 | ... = ... | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:61:5:61:5 | x indirection | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | clearning.cpp:62:8:62:8 | s indirection [x indirection] | +| clearning.cpp:61:3:61:7 | ... ++ indirection | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:61:3:61:7 | ... ++ indirection | clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | +| clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | clearning.cpp:62:8:62:8 | s indirection [x indirection] | +| clearning.cpp:61:5:61:5 | x indirection | clearning.cpp:61:3:61:7 | ... ++ indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:62:10:62:10 | x indirection | clearning.cpp:62:10:62:10 | x indirection | +| clearning.cpp:74:20:74:22 | argument_source output argument | clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | +| clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | clearning.cpp:76:8:76:8 | s indirection [val indirection] | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:10:76:12 | val indirection | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | clearning.cpp:76:10:76:12 | val indirection | +| clearning.cpp:76:10:76:12 | val indirection | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:76:10:76:12 | val indirection | clearning.cpp:76:7:76:12 | * ... | +| clearning.cpp:81:20:81:22 | argument_source output argument | clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | +| clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | clearning.cpp:83:13:83:13 | s indirection [val indirection] | +| clearning.cpp:83:5:83:21 | ... = ... indirection | clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | +| clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | clearning.cpp:84:8:84:8 | s indirection [val indirection] | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | clearning.cpp:83:13:83:21 | ... + ... indirection | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | clearning.cpp:83:15:83:17 | val indirection | +| clearning.cpp:83:13:83:21 | ... + ... indirection | clearning.cpp:83:5:83:21 | ... = ... indirection | +| clearning.cpp:83:15:83:17 | val indirection | clearning.cpp:83:5:83:21 | ... = ... indirection | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:10:84:12 | val indirection | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | clearning.cpp:84:10:84:12 | val indirection | +| clearning.cpp:84:10:84:12 | val indirection | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:84:10:84:12 | val indirection | clearning.cpp:84:7:84:12 | * ... | +| clearning.cpp:89:20:89:22 | argument_source output argument | clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | +| clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | clearning.cpp:90:3:90:3 | s indirection [val indirection] | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:90:5:90:7 | val indirection | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | clearning.cpp:91:8:91:8 | s indirection [val indirection] | +| clearning.cpp:90:3:90:9 | ... ++ indirection | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:90:3:90:9 | ... ++ indirection | clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | +| clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | clearning.cpp:91:8:91:8 | s indirection [val indirection] | +| clearning.cpp:90:5:90:7 | val indirection | clearning.cpp:90:3:90:9 | ... ++ indirection | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:10:91:12 | val indirection | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | clearning.cpp:91:10:91:12 | val indirection | +| clearning.cpp:91:10:91:12 | val indirection | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:91:10:91:12 | val indirection | clearning.cpp:91:7:91:12 | * ... | +| clearning.cpp:96:20:96:22 | argument_source output argument | clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | +| clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | clearning.cpp:97:10:97:10 | s indirection [val indirection] | +| clearning.cpp:97:2:97:18 | ... = ... indirection | clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | +| clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | clearning.cpp:98:8:98:8 | s indirection [val indirection] | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | clearning.cpp:97:10:97:18 | ... + ... indirection | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | clearning.cpp:97:12:97:14 | val indirection | +| clearning.cpp:97:10:97:18 | ... + ... indirection | clearning.cpp:97:2:97:18 | ... = ... indirection | +| clearning.cpp:97:12:97:14 | val indirection | clearning.cpp:97:2:97:18 | ... = ... indirection | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:10:98:12 | val indirection | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | clearning.cpp:98:10:98:12 | val indirection | +| clearning.cpp:98:10:98:12 | val indirection | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:98:10:98:12 | val indirection | clearning.cpp:98:7:98:12 | * ... | +| clearning.cpp:103:20:103:22 | argument_source output argument | clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | +| clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | clearning.cpp:104:2:104:2 | s indirection [val indirection] | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:104:4:104:6 | val indirection | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | clearning.cpp:105:8:105:8 | s indirection [val indirection] | +| clearning.cpp:104:2:104:8 | ... ++ indirection | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:104:2:104:8 | ... ++ indirection | clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | +| clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | clearning.cpp:105:8:105:8 | s indirection [val indirection] | +| clearning.cpp:104:4:104:6 | val indirection | clearning.cpp:104:2:104:8 | ... ++ indirection | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:10:105:12 | val indirection | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | clearning.cpp:105:10:105:12 | val indirection | +| clearning.cpp:105:10:105:12 | val indirection | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:105:10:105:12 | val indirection | clearning.cpp:105:7:105:12 | * ... | +| clearning.cpp:110:20:110:22 | argument_source output argument | clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | +| clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | clearning.cpp:111:4:111:4 | s indirection [val indirection] | +| clearning.cpp:111:2:111:8 | ++ ... indirection | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:111:2:111:8 | ++ ... indirection | clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:111:6:111:8 | val indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | clearning.cpp:112:8:112:8 | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | clearning.cpp:112:8:112:8 | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | val indirection | clearning.cpp:111:2:111:8 | ++ ... indirection | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:10:112:12 | val indirection | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | clearning.cpp:112:10:112:12 | val indirection | +| clearning.cpp:112:10:112:12 | val indirection | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:112:10:112:12 | val indirection | clearning.cpp:112:7:112:12 | * ... | +| clearning.cpp:117:20:117:22 | argument_source output argument | clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | +| clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | clearning.cpp:118:2:118:2 | s indirection [val indirection] | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:118:4:118:6 | val indirection | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | clearning.cpp:119:8:119:8 | s indirection [val indirection] | +| clearning.cpp:118:2:118:11 | ... += ... indirection | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:118:2:118:11 | ... += ... indirection | clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | +| clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | clearning.cpp:119:8:119:8 | s indirection [val indirection] | +| clearning.cpp:118:4:118:6 | val indirection | clearning.cpp:118:2:118:11 | ... += ... indirection | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:10:119:12 | val indirection | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | clearning.cpp:119:10:119:12 | val indirection | +| clearning.cpp:119:10:119:12 | val indirection | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:119:10:119:12 | val indirection | clearning.cpp:119:7:119:12 | * ... | +| clearning.cpp:151:3:151:22 | ... = ... | clearning.cpp:151:5:151:7 | s indirection [post update] [val] | +| clearning.cpp:151:5:151:7 | s indirection [post update] [val] | clearning.cpp:152:8:152:8 | s indirection [val] | +| clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:151:3:151:22 | ... = ... | +| clearning.cpp:152:8:152:8 | s indirection [val] | clearning.cpp:152:10:152:12 | val | +| clearning.cpp:152:8:152:8 | s indirection [val] | clearning.cpp:152:10:152:12 | val indirection | +| clearning.cpp:152:10:152:12 | val indirection | clearning.cpp:152:10:152:12 | val | | complex.cpp:9:7:9:7 | this indirection [a_] | complex.cpp:9:20:9:21 | this indirection [a_] | | complex.cpp:9:20:9:21 | a_ | complex.cpp:9:7:9:7 | a indirection | | complex.cpp:9:20:9:21 | a_ indirection | complex.cpp:9:7:9:7 | a indirection | @@ -861,19 +991,20 @@ edges | struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a | | struct_init.c:15:8:15:9 | ab indirection [a] | struct_init.c:15:12:15:12 | a indirection | | struct_init.c:15:12:15:12 | a indirection | struct_init.c:15:12:15:12 | a | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:22:8:22:9 | ab indirection [a] | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:24:10:24:12 | & ... indirection [a] | -| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:28:5:28:7 | & ... indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:22:8:22:9 | ab indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:24:10:24:12 | & ... indirection [a] | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | struct_init.c:28:5:28:7 | & ... indirection [a] | +| struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | struct_init.c:20:13:20:14 | definition of ab indirection [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | | struct_init.c:20:20:20:29 | call to user_input | struct_init.c:20:20:20:29 | call to user_input | | struct_init.c:22:8:22:9 | ab indirection [a] | struct_init.c:22:11:22:11 | a | | struct_init.c:22:8:22:9 | ab indirection [a] | struct_init.c:22:11:22:11 | a indirection | | struct_init.c:22:11:22:11 | a indirection | struct_init.c:22:11:22:11 | a | | struct_init.c:24:10:24:12 | & ... indirection [a] | struct_init.c:14:24:14:25 | ab indirection [a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | -| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | struct_init.c:31:8:31:12 | outer indirection [nestedAB, a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | +| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | +| struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [pointerAB indirection, a] | struct_init.c:33:8:33:12 | outer indirection [pointerAB indirection, a] | | struct_init.c:27:5:27:23 | {...} indirection [post update] [a] | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:27:5:27:23 | {...} indirection [post update] [a] | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | @@ -892,7 +1023,8 @@ edges | struct_init.c:33:25:33:25 | a indirection | struct_init.c:33:25:33:25 | a | | struct_init.c:36:10:36:24 | & ... indirection [a] | struct_init.c:14:24:14:25 | ab indirection [a] | | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | struct_init.c:36:10:36:24 | & ... indirection [a] | -| struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | struct_init.c:43:5:43:7 | & ... indirection [a] | +| struct_init.c:40:13:40:14 | definition of ab indirection [a] | struct_init.c:43:5:43:7 | & ... indirection [a] | +| struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | struct_init.c:40:13:40:14 | definition of ab indirection [a] | | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | | struct_init.c:40:20:40:29 | call to user_input | struct_init.c:40:20:40:29 | call to user_input | | struct_init.c:41:23:44:3 | definition of outer indirection [post update] [pointerAB indirection, a] | struct_init.c:46:10:46:14 | outer indirection [pointerAB indirection, a] | @@ -1433,6 +1565,114 @@ nodes | by_reference.cpp:136:8:136:13 | pouter indirection [a] | semmle.label | pouter indirection [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | | by_reference.cpp:136:16:136:16 | a indirection | semmle.label | a indirection | +| clearning.cpp:32:3:32:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:32:6:32:6 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:32:10:32:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:33:5:33:5 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:34:8:34:11 | * ... | semmle.label | * ... | +| clearning.cpp:34:9:34:9 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:34:11:34:11 | x indirection | semmle.label | x indirection | +| clearning.cpp:34:11:34:11 | x indirection | semmle.label | x indirection | +| clearning.cpp:53:3:53:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:53:6:53:6 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:53:10:53:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:54:3:54:3 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:54:3:54:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:54:3:54:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:54:5:54:5 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:54:5:54:5 | x indirection | semmle.label | x indirection | +| clearning.cpp:55:8:55:8 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:55:10:55:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:55:10:55:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:60:3:60:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:60:7:60:7 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:60:11:60:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:61:3:61:3 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:61:3:61:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:61:3:61:7 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:61:5:61:5 | s indirection [post update] [x indirection] | semmle.label | s indirection [post update] [x indirection] | +| clearning.cpp:61:5:61:5 | x indirection | semmle.label | x indirection | +| clearning.cpp:62:8:62:8 | s indirection [x indirection] | semmle.label | s indirection [x indirection] | +| clearning.cpp:62:10:62:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:62:10:62:10 | x indirection | semmle.label | x indirection | +| clearning.cpp:74:20:74:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:74:20:74:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:76:7:76:12 | * ... | semmle.label | * ... | +| clearning.cpp:76:8:76:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:76:10:76:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:76:10:76:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:81:20:81:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:81:20:81:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:83:5:83:21 | ... = ... indirection | semmle.label | ... = ... indirection | +| clearning.cpp:83:7:83:9 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:83:13:83:13 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:83:13:83:21 | ... + ... indirection | semmle.label | ... + ... indirection | +| clearning.cpp:83:15:83:17 | val indirection | semmle.label | val indirection | +| clearning.cpp:84:7:84:12 | * ... | semmle.label | * ... | +| clearning.cpp:84:8:84:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:84:10:84:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:84:10:84:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:89:20:89:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:89:20:89:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:90:3:90:3 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:90:3:90:9 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:90:3:90:9 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:90:5:90:7 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:90:5:90:7 | val indirection | semmle.label | val indirection | +| clearning.cpp:91:7:91:12 | * ... | semmle.label | * ... | +| clearning.cpp:91:8:91:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:91:10:91:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:91:10:91:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:96:20:96:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:96:20:96:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:97:2:97:18 | ... = ... indirection | semmle.label | ... = ... indirection | +| clearning.cpp:97:4:97:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:97:10:97:10 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:97:10:97:18 | ... + ... indirection | semmle.label | ... + ... indirection | +| clearning.cpp:97:12:97:14 | val indirection | semmle.label | val indirection | +| clearning.cpp:98:7:98:12 | * ... | semmle.label | * ... | +| clearning.cpp:98:8:98:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:98:10:98:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:98:10:98:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:103:20:103:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:103:20:103:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:104:2:104:2 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:104:2:104:8 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:104:2:104:8 | ... ++ indirection | semmle.label | ... ++ indirection | +| clearning.cpp:104:4:104:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:104:4:104:6 | val indirection | semmle.label | val indirection | +| clearning.cpp:105:7:105:12 | * ... | semmle.label | * ... | +| clearning.cpp:105:8:105:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:105:10:105:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:105:10:105:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:110:20:110:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:110:20:110:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:111:2:111:8 | ++ ... indirection | semmle.label | ++ ... indirection | +| clearning.cpp:111:2:111:8 | ++ ... indirection | semmle.label | ++ ... indirection | +| clearning.cpp:111:4:111:4 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:111:6:111:8 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:111:6:111:8 | val indirection | semmle.label | val indirection | +| clearning.cpp:112:7:112:12 | * ... | semmle.label | * ... | +| clearning.cpp:112:8:112:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:112:10:112:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:112:10:112:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:117:20:117:22 | argument_source output argument | semmle.label | argument_source output argument | +| clearning.cpp:117:20:117:22 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:118:2:118:2 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:118:2:118:11 | ... += ... indirection | semmle.label | ... += ... indirection | +| clearning.cpp:118:2:118:11 | ... += ... indirection | semmle.label | ... += ... indirection | +| clearning.cpp:118:4:118:6 | s indirection [post update] [val indirection] | semmle.label | s indirection [post update] [val indirection] | +| clearning.cpp:118:4:118:6 | val indirection | semmle.label | val indirection | +| clearning.cpp:119:7:119:12 | * ... | semmle.label | * ... | +| clearning.cpp:119:8:119:8 | s indirection [val indirection] | semmle.label | s indirection [val indirection] | +| clearning.cpp:119:10:119:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:119:10:119:12 | val indirection | semmle.label | val indirection | +| clearning.cpp:151:3:151:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:151:5:151:7 | s indirection [post update] [val] | semmle.label | s indirection [post update] [val] | +| clearning.cpp:151:11:151:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:152:8:152:8 | s indirection [val] | semmle.label | s indirection [val] | +| clearning.cpp:152:10:152:12 | val | semmle.label | val | +| clearning.cpp:152:10:152:12 | val indirection | semmle.label | val indirection | | complex.cpp:9:7:9:7 | a indirection | semmle.label | a indirection | | complex.cpp:9:7:9:7 | this indirection [a_] | semmle.label | this indirection [a_] | | complex.cpp:9:20:9:21 | a_ | semmle.label | a_ | @@ -1699,6 +1939,7 @@ nodes | struct_init.c:15:8:15:9 | ab indirection [a] | semmle.label | ab indirection [a] | | struct_init.c:15:12:15:12 | a | semmle.label | a | | struct_init.c:15:12:15:12 | a indirection | semmle.label | a indirection | +| struct_init.c:20:13:20:14 | definition of ab indirection [a] | semmle.label | definition of ab indirection [a] | | struct_init.c:20:17:20:36 | definition of ab indirection [post update] [a] | semmle.label | definition of ab indirection [post update] [a] | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:20:20:20:29 | call to user_input | semmle.label | call to user_input | @@ -1706,6 +1947,7 @@ nodes | struct_init.c:22:11:22:11 | a | semmle.label | a | | struct_init.c:22:11:22:11 | a indirection | semmle.label | a indirection | | struct_init.c:24:10:24:12 | & ... indirection [a] | semmle.label | & ... indirection [a] | +| struct_init.c:26:16:26:20 | definition of outer indirection [nestedAB, a] | semmle.label | definition of outer indirection [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | semmle.label | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [nestedAB, a] | semmle.label | definition of outer indirection [post update] [nestedAB, a] | | struct_init.c:26:23:29:3 | definition of outer indirection [post update] [pointerAB indirection, a] | semmle.label | definition of outer indirection [post update] [pointerAB indirection, a] | @@ -1724,6 +1966,7 @@ nodes | struct_init.c:33:25:33:25 | a indirection | semmle.label | a indirection | | struct_init.c:36:10:36:24 | & ... indirection [a] | semmle.label | & ... indirection [a] | | struct_init.c:36:11:36:15 | outer indirection [nestedAB, a] | semmle.label | outer indirection [nestedAB, a] | +| struct_init.c:40:13:40:14 | definition of ab indirection [a] | semmle.label | definition of ab indirection [a] | | struct_init.c:40:17:40:36 | definition of ab indirection [post update] [a] | semmle.label | definition of ab indirection [post update] [a] | | struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input | | struct_init.c:40:20:40:29 | call to user_input | semmle.label | call to user_input | @@ -1883,6 +2126,17 @@ subpaths | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | +| clearning.cpp:34:8:34:11 | * ... | clearning.cpp:32:10:32:19 | call to user_input | clearning.cpp:34:8:34:11 | * ... | * ... flows from $@ | clearning.cpp:32:10:32:19 | call to user_input | call to user_input | +| clearning.cpp:55:10:55:10 | x indirection | clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:55:10:55:10 | x indirection | x indirection flows from $@ | clearning.cpp:53:10:53:19 | call to user_input | call to user_input | +| clearning.cpp:62:10:62:10 | x indirection | clearning.cpp:60:11:60:20 | call to user_input | clearning.cpp:62:10:62:10 | x indirection | x indirection flows from $@ | clearning.cpp:60:11:60:20 | call to user_input | call to user_input | +| clearning.cpp:76:7:76:12 | * ... | clearning.cpp:74:20:74:22 | argument_source output argument | clearning.cpp:76:7:76:12 | * ... | * ... flows from $@ | clearning.cpp:74:20:74:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:84:7:84:12 | * ... | clearning.cpp:81:20:81:22 | argument_source output argument | clearning.cpp:84:7:84:12 | * ... | * ... flows from $@ | clearning.cpp:81:20:81:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:91:7:91:12 | * ... | clearning.cpp:89:20:89:22 | argument_source output argument | clearning.cpp:91:7:91:12 | * ... | * ... flows from $@ | clearning.cpp:89:20:89:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:98:7:98:12 | * ... | clearning.cpp:96:20:96:22 | argument_source output argument | clearning.cpp:98:7:98:12 | * ... | * ... flows from $@ | clearning.cpp:96:20:96:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:105:7:105:12 | * ... | clearning.cpp:103:20:103:22 | argument_source output argument | clearning.cpp:105:7:105:12 | * ... | * ... flows from $@ | clearning.cpp:103:20:103:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:112:7:112:12 | * ... | clearning.cpp:110:20:110:22 | argument_source output argument | clearning.cpp:112:7:112:12 | * ... | * ... flows from $@ | clearning.cpp:110:20:110:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:119:7:119:12 | * ... | clearning.cpp:117:20:117:22 | argument_source output argument | clearning.cpp:119:7:119:12 | * ... | * ... flows from $@ | clearning.cpp:117:20:117:22 | argument_source output argument | argument_source output argument | +| clearning.cpp:152:10:152:12 | val | clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:152:10:152:12 | val | val flows from $@ | clearning.cpp:151:11:151:20 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | | complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected index 6749e278fba..ad76accce67 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-diff.expected @@ -167,6 +167,66 @@ | by_reference.cpp:88:9:88:9 | a | AST only | | by_reference.cpp:92:3:92:5 | * ... | AST only | | by_reference.cpp:96:3:96:4 | pa | AST only | +| clearning.cpp:18:7:18:7 | s | IR only | +| clearning.cpp:19:3:19:6 | * ... | AST only | +| clearning.cpp:20:12:20:12 | s | IR only | +| clearning.cpp:25:7:25:7 | s | IR only | +| clearning.cpp:26:7:26:7 | s | IR only | +| clearning.cpp:27:12:27:12 | s | IR only | +| clearning.cpp:32:3:32:6 | * ... | AST only | +| clearning.cpp:33:7:33:7 | s | IR only | +| clearning.cpp:34:8:34:11 | * ... | IR only | +| clearning.cpp:34:11:34:11 | s | IR only | +| clearning.cpp:39:3:39:6 | * ... | AST only | +| clearning.cpp:40:5:40:5 | x | AST only | +| clearning.cpp:41:8:41:11 | * ... | IR only | +| clearning.cpp:41:11:41:11 | s | IR only | +| clearning.cpp:46:7:46:7 | s | IR only | +| clearning.cpp:47:5:47:5 | x | AST only | +| clearning.cpp:48:8:48:11 | * ... | IR only | +| clearning.cpp:48:11:48:11 | s | IR only | +| clearning.cpp:53:3:53:6 | * ... | AST only | +| clearning.cpp:54:5:54:5 | x | AST only | +| clearning.cpp:60:7:60:7 | s | IR only | +| clearning.cpp:61:5:61:5 | x | AST only | +| clearning.cpp:75:2:75:10 | access to array | AST only | +| clearning.cpp:76:10:76:12 | s | IR only | +| clearning.cpp:82:2:82:9 | access to array | AST only | +| clearning.cpp:83:7:83:9 | val | AST only | +| clearning.cpp:83:15:83:17 | s | IR only | +| clearning.cpp:84:10:84:12 | s | IR only | +| clearning.cpp:90:5:90:7 | val | AST only | +| clearning.cpp:91:10:91:12 | s | IR only | +| clearning.cpp:97:4:97:6 | val | AST only | +| clearning.cpp:97:12:97:14 | s | IR only | +| clearning.cpp:98:10:98:12 | s | IR only | +| clearning.cpp:104:4:104:6 | val | AST only | +| clearning.cpp:105:10:105:12 | s | IR only | +| clearning.cpp:111:6:111:8 | val | AST only | +| clearning.cpp:112:10:112:12 | s | IR only | +| clearning.cpp:118:4:118:6 | val | AST only | +| clearning.cpp:119:10:119:12 | s | IR only | +| clearning.cpp:124:4:124:6 | val | AST only | +| clearning.cpp:125:4:125:6 | val | AST only | +| clearning.cpp:131:4:131:6 | val | AST only | +| clearning.cpp:132:4:132:6 | val | AST only | +| clearning.cpp:138:4:138:6 | val | AST only | +| clearning.cpp:139:6:139:8 | val | AST only | +| clearning.cpp:151:5:151:7 | val | AST only | +| clearning.cpp:152:10:152:12 | s | IR only | +| clearning.cpp:157:5:157:7 | val | AST only | +| clearning.cpp:158:5:158:7 | val | AST only | +| clearning.cpp:159:10:159:12 | s | IR only | +| clearning.cpp:164:5:164:7 | val | AST only | +| clearning.cpp:165:5:165:7 | val | AST only | +| clearning.cpp:166:10:166:12 | s | IR only | +| clearning.cpp:171:5:171:7 | val | AST only | +| clearning.cpp:172:5:172:7 | val | AST only | +| clearning.cpp:173:10:173:12 | s | IR only | +| clearning.cpp:178:5:178:7 | val | AST only | +| clearning.cpp:179:5:179:7 | val | AST only | +| clearning.cpp:179:13:179:15 | s | IR only | +| clearning.cpp:180:10:180:12 | s | IR only | | complex.cpp:9:20:9:21 | this | IR only | | complex.cpp:10:20:10:21 | this | IR only | | complex.cpp:11:22:11:23 | a_ | AST only | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected index 1818cea24bb..823997fd7d3 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition-ir.expected @@ -408,6 +408,90 @@ | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter | | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:18:5:18:5 | s | +| clearning.cpp:19:4:19:4 | s | +| clearning.cpp:20:10:20:10 | s | +| clearning.cpp:25:5:25:5 | s | +| clearning.cpp:26:5:26:5 | s | +| clearning.cpp:27:10:27:10 | s | +| clearning.cpp:32:4:32:4 | s | +| clearning.cpp:33:5:33:5 | s | +| clearning.cpp:34:8:34:11 | * ... | +| clearning.cpp:34:9:34:9 | s | +| clearning.cpp:39:4:39:4 | s | +| clearning.cpp:40:3:40:3 | s | +| clearning.cpp:41:8:41:11 | * ... | +| clearning.cpp:41:9:41:9 | s | +| clearning.cpp:46:5:46:5 | s | +| clearning.cpp:47:3:47:3 | s | +| clearning.cpp:48:8:48:11 | * ... | +| clearning.cpp:48:9:48:9 | s | +| clearning.cpp:53:4:53:4 | s | +| clearning.cpp:54:3:54:3 | s | +| clearning.cpp:55:8:55:8 | s | +| clearning.cpp:55:10:55:10 | x | +| clearning.cpp:60:5:60:5 | s | +| clearning.cpp:61:3:61:3 | s | +| clearning.cpp:62:8:62:8 | s | +| clearning.cpp:62:10:62:10 | x | +| clearning.cpp:74:18:74:18 | s | +| clearning.cpp:74:20:74:22 | val | +| clearning.cpp:75:2:75:2 | s | +| clearning.cpp:76:8:76:8 | s | +| clearning.cpp:81:18:81:18 | s | +| clearning.cpp:81:20:81:22 | val | +| clearning.cpp:82:2:82:2 | s | +| clearning.cpp:83:5:83:5 | s | +| clearning.cpp:83:13:83:13 | s | +| clearning.cpp:84:8:84:8 | s | +| clearning.cpp:89:18:89:18 | s | +| clearning.cpp:89:20:89:22 | val | +| clearning.cpp:90:3:90:3 | s | +| clearning.cpp:91:8:91:8 | s | +| clearning.cpp:96:18:96:18 | s | +| clearning.cpp:96:20:96:22 | val | +| clearning.cpp:97:2:97:2 | s | +| clearning.cpp:97:10:97:10 | s | +| clearning.cpp:98:8:98:8 | s | +| clearning.cpp:103:18:103:18 | s | +| clearning.cpp:103:20:103:22 | val | +| clearning.cpp:104:2:104:2 | s | +| clearning.cpp:105:8:105:8 | s | +| clearning.cpp:110:18:110:18 | s | +| clearning.cpp:110:20:110:22 | val | +| clearning.cpp:111:4:111:4 | s | +| clearning.cpp:112:8:112:8 | s | +| clearning.cpp:117:18:117:18 | s | +| clearning.cpp:117:20:117:22 | val | +| clearning.cpp:118:2:118:2 | s | +| clearning.cpp:119:8:119:8 | s | +| clearning.cpp:124:2:124:2 | s | +| clearning.cpp:125:2:125:2 | s | +| clearning.cpp:126:7:126:7 | s | +| clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s | +| clearning.cpp:132:2:132:2 | s | +| clearning.cpp:133:7:133:7 | s | +| clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s | +| clearning.cpp:139:4:139:4 | s | +| clearning.cpp:140:7:140:7 | s | +| clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s | +| clearning.cpp:152:8:152:8 | s | +| clearning.cpp:157:3:157:3 | s | +| clearning.cpp:158:3:158:3 | s | +| clearning.cpp:159:8:159:8 | s | +| clearning.cpp:164:3:164:3 | s | +| clearning.cpp:165:3:165:3 | s | +| clearning.cpp:166:8:166:8 | s | +| clearning.cpp:171:3:171:3 | s | +| clearning.cpp:172:3:172:3 | s | +| clearning.cpp:173:8:173:8 | s | +| clearning.cpp:178:3:178:3 | s | +| clearning.cpp:179:3:179:3 | s | +| clearning.cpp:179:11:179:11 | s | +| clearning.cpp:180:8:180:8 | s | | complex.cpp:9:20:9:21 | this | | complex.cpp:10:20:10:21 | this | | complex.cpp:11:22:11:23 | this | diff --git a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected index 373e357142a..c37eca67c75 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/partial-definition.expected @@ -348,6 +348,92 @@ | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter | | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:19:3:19:6 | * ... | +| clearning.cpp:19:4:19:4 | s | +| clearning.cpp:32:3:32:6 | * ... | +| clearning.cpp:32:4:32:4 | s | +| clearning.cpp:39:3:39:6 | * ... | +| clearning.cpp:39:4:39:4 | s | +| clearning.cpp:40:3:40:3 | s | +| clearning.cpp:40:5:40:5 | x | +| clearning.cpp:47:3:47:3 | s | +| clearning.cpp:47:5:47:5 | x | +| clearning.cpp:53:3:53:6 | * ... | +| clearning.cpp:53:4:53:4 | s | +| clearning.cpp:54:3:54:3 | s | +| clearning.cpp:54:5:54:5 | x | +| clearning.cpp:55:8:55:8 | s | +| clearning.cpp:55:10:55:10 | x | +| clearning.cpp:61:3:61:3 | s | +| clearning.cpp:61:5:61:5 | x | +| clearning.cpp:62:8:62:8 | s | +| clearning.cpp:62:10:62:10 | x | +| clearning.cpp:74:18:74:18 | s | +| clearning.cpp:74:20:74:22 | val | +| clearning.cpp:75:2:75:2 | s | +| clearning.cpp:75:2:75:10 | access to array | +| clearning.cpp:81:18:81:18 | s | +| clearning.cpp:81:20:81:22 | val | +| clearning.cpp:82:2:82:2 | s | +| clearning.cpp:82:2:82:9 | access to array | +| clearning.cpp:83:5:83:5 | s | +| clearning.cpp:83:7:83:9 | val | +| clearning.cpp:89:18:89:18 | s | +| clearning.cpp:89:20:89:22 | val | +| clearning.cpp:90:3:90:3 | s | +| clearning.cpp:90:5:90:7 | val | +| clearning.cpp:96:18:96:18 | s | +| clearning.cpp:96:20:96:22 | val | +| clearning.cpp:97:2:97:2 | s | +| clearning.cpp:97:4:97:6 | val | +| clearning.cpp:103:18:103:18 | s | +| clearning.cpp:103:20:103:22 | val | +| clearning.cpp:104:2:104:2 | s | +| clearning.cpp:104:4:104:6 | val | +| clearning.cpp:110:18:110:18 | s | +| clearning.cpp:110:20:110:22 | val | +| clearning.cpp:111:4:111:4 | s | +| clearning.cpp:111:6:111:8 | val | +| clearning.cpp:117:18:117:18 | s | +| clearning.cpp:117:20:117:22 | val | +| clearning.cpp:118:2:118:2 | s | +| clearning.cpp:118:4:118:6 | val | +| clearning.cpp:124:2:124:2 | s | +| clearning.cpp:124:4:124:6 | val | +| clearning.cpp:125:2:125:2 | s | +| clearning.cpp:125:4:125:6 | val | +| clearning.cpp:126:7:126:7 | s | +| clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s | +| clearning.cpp:131:4:131:6 | val | +| clearning.cpp:132:2:132:2 | s | +| clearning.cpp:132:4:132:6 | val | +| clearning.cpp:133:7:133:7 | s | +| clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s | +| clearning.cpp:138:4:138:6 | val | +| clearning.cpp:139:4:139:4 | s | +| clearning.cpp:139:6:139:8 | val | +| clearning.cpp:140:7:140:7 | s | +| clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s | +| clearning.cpp:151:5:151:7 | val | +| clearning.cpp:157:3:157:3 | s | +| clearning.cpp:157:5:157:7 | val | +| clearning.cpp:158:3:158:3 | s | +| clearning.cpp:158:5:158:7 | val | +| clearning.cpp:164:3:164:3 | s | +| clearning.cpp:164:5:164:7 | val | +| clearning.cpp:165:3:165:3 | s | +| clearning.cpp:165:5:165:7 | val | +| clearning.cpp:171:3:171:3 | s | +| clearning.cpp:171:5:171:7 | val | +| clearning.cpp:172:3:172:3 | s | +| clearning.cpp:172:5:172:7 | val | +| clearning.cpp:178:3:178:3 | s | +| clearning.cpp:178:5:178:7 | val | +| clearning.cpp:179:3:179:3 | s | +| clearning.cpp:179:5:179:7 | val | | complex.cpp:11:22:11:23 | a_ | | complex.cpp:11:22:11:23 | this | | complex.cpp:12:22:12:23 | b_ | diff --git a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected index 9eef2881545..00a5f1a3f28 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected +++ b/cpp/ql/test/library-tests/dataflow/fields/path-flow.expected @@ -448,6 +448,42 @@ edges | by_reference.cpp:135:8:135:13 | pouter [inner_ptr, a] | by_reference.cpp:135:16:135:24 | inner_ptr [a] | | by_reference.cpp:135:16:135:24 | inner_ptr [a] | by_reference.cpp:135:27:135:27 | a | | by_reference.cpp:136:8:136:13 | pouter [a] | by_reference.cpp:136:16:136:16 | a | +| clearning.cpp:53:4:53:4 | s [post update] [x] | clearning.cpp:55:8:55:8 | s [x] | +| clearning.cpp:53:6:53:6 | x [inner post update] | clearning.cpp:53:4:53:4 | s [post update] [x] | +| clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:53:6:53:6 | x [inner post update] | +| clearning.cpp:55:8:55:8 | s [x] | clearning.cpp:55:10:55:10 | x | +| clearning.cpp:124:2:124:2 | s [post update] [val] | clearning.cpp:126:7:126:7 | s [val] | +| clearning.cpp:124:2:124:25 | ... = ... | clearning.cpp:124:2:124:2 | s [post update] [val] | +| clearning.cpp:124:10:124:19 | call to user_input | clearning.cpp:124:2:124:25 | ... = ... | +| clearning.cpp:126:7:126:7 | s [val] | clearning.cpp:126:9:126:11 | val | +| clearning.cpp:131:2:131:2 | s [post update] [val] | clearning.cpp:133:7:133:7 | s [val] | +| clearning.cpp:131:2:131:25 | ... = ... | clearning.cpp:131:2:131:2 | s [post update] [val] | +| clearning.cpp:131:10:131:19 | call to user_input | clearning.cpp:131:2:131:25 | ... = ... | +| clearning.cpp:133:7:133:7 | s [val] | clearning.cpp:133:9:133:11 | val | +| clearning.cpp:138:2:138:2 | s [post update] [val] | clearning.cpp:140:7:140:7 | s [val] | +| clearning.cpp:138:2:138:25 | ... = ... | clearning.cpp:138:2:138:2 | s [post update] [val] | +| clearning.cpp:138:10:138:19 | call to user_input | clearning.cpp:138:2:138:25 | ... = ... | +| clearning.cpp:140:7:140:7 | s [val] | clearning.cpp:140:9:140:11 | val | +| clearning.cpp:151:3:151:3 | s [post update] [val] | clearning.cpp:152:8:152:8 | s [val] | +| clearning.cpp:151:3:151:22 | ... = ... | clearning.cpp:151:3:151:3 | s [post update] [val] | +| clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:151:3:151:22 | ... = ... | +| clearning.cpp:152:8:152:8 | s [val] | clearning.cpp:152:10:152:12 | val | +| clearning.cpp:157:3:157:3 | s [post update] [val] | clearning.cpp:159:8:159:8 | s [val] | +| clearning.cpp:157:3:157:22 | ... = ... | clearning.cpp:157:3:157:3 | s [post update] [val] | +| clearning.cpp:157:11:157:20 | call to user_input | clearning.cpp:157:3:157:22 | ... = ... | +| clearning.cpp:159:8:159:8 | s [val] | clearning.cpp:159:10:159:12 | val | +| clearning.cpp:164:3:164:3 | s [post update] [val] | clearning.cpp:166:8:166:8 | s [val] | +| clearning.cpp:164:3:164:22 | ... = ... | clearning.cpp:164:3:164:3 | s [post update] [val] | +| clearning.cpp:164:11:164:20 | call to user_input | clearning.cpp:164:3:164:22 | ... = ... | +| clearning.cpp:166:8:166:8 | s [val] | clearning.cpp:166:10:166:12 | val | +| clearning.cpp:171:3:171:3 | s [post update] [val] | clearning.cpp:173:8:173:8 | s [val] | +| clearning.cpp:171:3:171:22 | ... = ... | clearning.cpp:171:3:171:3 | s [post update] [val] | +| clearning.cpp:171:11:171:20 | call to user_input | clearning.cpp:171:3:171:22 | ... = ... | +| clearning.cpp:173:8:173:8 | s [val] | clearning.cpp:173:10:173:12 | val | +| clearning.cpp:178:3:178:3 | s [post update] [val] | clearning.cpp:180:8:180:8 | s [val] | +| clearning.cpp:178:3:178:22 | ... = ... | clearning.cpp:178:3:178:3 | s [post update] [val] | +| clearning.cpp:178:11:178:20 | call to user_input | clearning.cpp:178:3:178:22 | ... = ... | +| clearning.cpp:180:8:180:8 | s [val] | clearning.cpp:180:10:180:12 | val | | complex.cpp:9:7:9:7 | this [a_] | complex.cpp:9:20:9:21 | this [a_] | | complex.cpp:9:20:9:21 | this [a_] | complex.cpp:9:20:9:21 | a_ | | complex.cpp:10:7:10:7 | this [b_] | complex.cpp:10:20:10:21 | this [b_] | @@ -1155,6 +1191,51 @@ nodes | by_reference.cpp:135:27:135:27 | a | semmle.label | a | | by_reference.cpp:136:8:136:13 | pouter [a] | semmle.label | pouter [a] | | by_reference.cpp:136:16:136:16 | a | semmle.label | a | +| clearning.cpp:53:4:53:4 | s [post update] [x] | semmle.label | s [post update] [x] | +| clearning.cpp:53:6:53:6 | x [inner post update] | semmle.label | x [inner post update] | +| clearning.cpp:53:10:53:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:55:8:55:8 | s [x] | semmle.label | s [x] | +| clearning.cpp:55:10:55:10 | x | semmle.label | x | +| clearning.cpp:124:2:124:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:124:2:124:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:124:10:124:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:126:7:126:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:126:9:126:11 | val | semmle.label | val | +| clearning.cpp:131:2:131:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:131:2:131:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:131:10:131:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:133:7:133:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:133:9:133:11 | val | semmle.label | val | +| clearning.cpp:138:2:138:2 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:138:2:138:25 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:138:10:138:19 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:140:7:140:7 | s [val] | semmle.label | s [val] | +| clearning.cpp:140:9:140:11 | val | semmle.label | val | +| clearning.cpp:151:3:151:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:151:3:151:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:151:11:151:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:152:8:152:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:152:10:152:12 | val | semmle.label | val | +| clearning.cpp:157:3:157:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:157:3:157:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:157:11:157:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:159:8:159:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:159:10:159:12 | val | semmle.label | val | +| clearning.cpp:164:3:164:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:164:3:164:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:164:11:164:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:166:8:166:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:166:10:166:12 | val | semmle.label | val | +| clearning.cpp:171:3:171:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:171:3:171:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:171:11:171:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:173:8:173:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:173:10:173:12 | val | semmle.label | val | +| clearning.cpp:178:3:178:3 | s [post update] [val] | semmle.label | s [post update] [val] | +| clearning.cpp:178:3:178:22 | ... = ... | semmle.label | ... = ... | +| clearning.cpp:178:11:178:20 | call to user_input | semmle.label | call to user_input | +| clearning.cpp:180:8:180:8 | s [val] | semmle.label | s [val] | +| clearning.cpp:180:10:180:12 | val | semmle.label | val | | complex.cpp:9:7:9:7 | this [a_] | semmle.label | this [a_] | | complex.cpp:9:20:9:21 | a_ | semmle.label | a_ | | complex.cpp:9:20:9:21 | this [a_] | semmle.label | this [a_] | @@ -1551,6 +1632,15 @@ subpaths | by_reference.cpp:134:29:134:29 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:134:29:134:29 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:135:27:135:27 | a | by_reference.cpp:88:13:88:22 | call to user_input | by_reference.cpp:135:27:135:27 | a | a flows from $@ | by_reference.cpp:88:13:88:22 | call to user_input | call to user_input | | by_reference.cpp:136:16:136:16 | a | by_reference.cpp:96:8:96:17 | call to user_input | by_reference.cpp:136:16:136:16 | a | a flows from $@ | by_reference.cpp:96:8:96:17 | call to user_input | call to user_input | +| clearning.cpp:55:10:55:10 | x | clearning.cpp:53:10:53:19 | call to user_input | clearning.cpp:55:10:55:10 | x | x flows from $@ | clearning.cpp:53:10:53:19 | call to user_input | call to user_input | +| clearning.cpp:126:9:126:11 | val | clearning.cpp:124:10:124:19 | call to user_input | clearning.cpp:126:9:126:11 | val | val flows from $@ | clearning.cpp:124:10:124:19 | call to user_input | call to user_input | +| clearning.cpp:133:9:133:11 | val | clearning.cpp:131:10:131:19 | call to user_input | clearning.cpp:133:9:133:11 | val | val flows from $@ | clearning.cpp:131:10:131:19 | call to user_input | call to user_input | +| clearning.cpp:140:9:140:11 | val | clearning.cpp:138:10:138:19 | call to user_input | clearning.cpp:140:9:140:11 | val | val flows from $@ | clearning.cpp:138:10:138:19 | call to user_input | call to user_input | +| clearning.cpp:152:10:152:12 | val | clearning.cpp:151:11:151:20 | call to user_input | clearning.cpp:152:10:152:12 | val | val flows from $@ | clearning.cpp:151:11:151:20 | call to user_input | call to user_input | +| clearning.cpp:159:10:159:12 | val | clearning.cpp:157:11:157:20 | call to user_input | clearning.cpp:159:10:159:12 | val | val flows from $@ | clearning.cpp:157:11:157:20 | call to user_input | call to user_input | +| clearning.cpp:166:10:166:12 | val | clearning.cpp:164:11:164:20 | call to user_input | clearning.cpp:166:10:166:12 | val | val flows from $@ | clearning.cpp:164:11:164:20 | call to user_input | call to user_input | +| clearning.cpp:173:10:173:12 | val | clearning.cpp:171:11:171:20 | call to user_input | clearning.cpp:173:10:173:12 | val | val flows from $@ | clearning.cpp:171:11:171:20 | call to user_input | call to user_input | +| clearning.cpp:180:10:180:12 | val | clearning.cpp:178:11:178:20 | call to user_input | clearning.cpp:180:10:180:12 | val | val flows from $@ | clearning.cpp:178:11:178:20 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:53:19:53:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:53:19:53:28 | call to user_input | call to user_input | | complex.cpp:42:18:42:18 | call to a | complex.cpp:55:19:55:28 | call to user_input | complex.cpp:42:18:42:18 | call to a | call to a flows from $@ | complex.cpp:55:19:55:28 | call to user_input | call to user_input | | complex.cpp:43:18:43:18 | call to b | complex.cpp:54:19:54:28 | call to user_input | complex.cpp:43:18:43:18 | call to b | call to b flows from $@ | complex.cpp:54:19:54:28 | call to user_input | call to user_input | diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected index 528d164b888..e1665c23315 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected @@ -1,8 +1,9 @@ edges | test.cpp:16:11:16:21 | mk_string_t indirection [string] | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | | test.cpp:18:5:18:30 | ... = ... | test.cpp:18:10:18:15 | str indirection [post update] [string] | -| test.cpp:18:10:18:15 | str indirection [post update] [string] | test.cpp:16:11:16:21 | mk_string_t indirection [string] | +| test.cpp:18:10:18:15 | str indirection [post update] [string] | test.cpp:19:5:19:7 | str indirection [string] | | test.cpp:18:19:18:24 | call to malloc | test.cpp:18:5:18:30 | ... = ... | +| test.cpp:19:5:19:7 | str indirection [string] | test.cpp:16:11:16:21 | mk_string_t indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:42:13:42:15 | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:72:17:72:19 | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | test.cpp:80:17:80:19 | str indirection [string] | @@ -17,8 +18,9 @@ edges | test.cpp:80:22:80:27 | string indirection | test.cpp:80:22:80:27 | string | | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | | test.cpp:90:5:90:34 | ... = ... | test.cpp:90:10:90:15 | str indirection [post update] [string] | -| test.cpp:90:10:90:15 | str indirection [post update] [string] | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | +| test.cpp:90:10:90:15 | str indirection [post update] [string] | test.cpp:91:5:91:7 | str indirection [string] | | test.cpp:90:19:90:24 | call to malloc | test.cpp:90:5:90:34 | ... = ... | +| test.cpp:91:5:91:7 | str indirection [string] | test.cpp:88:11:88:30 | mk_string_t_plus_one indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:99:13:99:15 | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:129:17:129:19 | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | test.cpp:137:17:137:19 | str indirection [string] | @@ -32,16 +34,17 @@ edges | test.cpp:137:17:137:19 | str indirection [string] | test.cpp:137:22:137:27 | string indirection | | test.cpp:137:22:137:27 | string indirection | test.cpp:137:22:137:27 | string | | test.cpp:147:5:147:34 | ... = ... | test.cpp:147:10:147:15 | str indirection [post update] [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:152:13:152:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:154:13:154:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:156:13:156:15 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:175:17:175:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:187:17:187:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:195:17:195:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:199:17:199:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:203:17:203:19 | str indirection [string] | -| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:207:17:207:19 | str indirection [string] | +| test.cpp:147:10:147:15 | str indirection [post update] [string] | test.cpp:148:5:148:7 | str indirection [string] | | test.cpp:147:19:147:24 | call to malloc | test.cpp:147:5:147:34 | ... = ... | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:152:13:152:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:154:13:154:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:156:13:156:15 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:175:17:175:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:187:17:187:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:195:17:195:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:199:17:199:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:203:17:203:19 | str indirection [string] | +| test.cpp:148:5:148:7 | str indirection [string] | test.cpp:207:17:207:19 | str indirection [string] | | test.cpp:152:13:152:15 | str indirection [string] | test.cpp:152:18:152:23 | string | | test.cpp:152:13:152:15 | str indirection [string] | test.cpp:152:18:152:23 | string indirection | | test.cpp:152:18:152:23 | string indirection | test.cpp:152:18:152:23 | string | @@ -91,6 +94,7 @@ nodes | test.cpp:18:5:18:30 | ... = ... | semmle.label | ... = ... | | test.cpp:18:10:18:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:18:19:18:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:19:5:19:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:39:21:39:31 | call to mk_string_t indirection [string] | semmle.label | call to mk_string_t indirection [string] | | test.cpp:42:13:42:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:42:18:42:23 | string | semmle.label | string | @@ -105,6 +109,7 @@ nodes | test.cpp:90:5:90:34 | ... = ... | semmle.label | ... = ... | | test.cpp:90:10:90:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:90:19:90:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:91:5:91:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:96:21:96:40 | call to mk_string_t_plus_one indirection [string] | semmle.label | call to mk_string_t_plus_one indirection [string] | | test.cpp:99:13:99:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:99:18:99:23 | string | semmle.label | string | @@ -118,6 +123,7 @@ nodes | test.cpp:147:5:147:34 | ... = ... | semmle.label | ... = ... | | test.cpp:147:10:147:15 | str indirection [post update] [string] | semmle.label | str indirection [post update] [string] | | test.cpp:147:19:147:24 | call to malloc | semmle.label | call to malloc | +| test.cpp:148:5:148:7 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:152:13:152:15 | str indirection [string] | semmle.label | str indirection [string] | | test.cpp:152:18:152:23 | string | semmle.label | string | | test.cpp:152:18:152:23 | string indirection | semmle.label | string indirection | From c7e5dc2e9e48dbd57561272da01a5f2fd4741097 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 26 Jun 2023 12:18:05 +0200 Subject: [PATCH 27/93] C++: Fix QLDoc issues --- .../code/cpp/ir/dataflow/internal/DataFlowUtil.qll | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll index 11a4b817dbf..209d0246832 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowUtil.qll @@ -1833,6 +1833,9 @@ class Content extends TContent { path = "" and sl = 0 and sc = 0 and el = 0 and ec = 0 } + /** Gets the indirection index of this `Content`. */ + abstract int getIndirectionIndex(); + /** * INTERNAL: Do not use. * @@ -1843,15 +1846,6 @@ class Content extends TContent { * the form `*f` is also cleared. */ abstract predicate impliesClearOf(Content c); - - abstract int getIndirectionIndex(); -} - -predicate foo(FieldContent f) { - exists(int i, Field ff | - i = f.getIndirectionIndex() and - ff = f.getField() - ) } /** A reference through a non-union instance field. */ @@ -1869,6 +1863,7 @@ class FieldContent extends Content, TFieldContent { Field getField() { result = f } + /** Gets the indirection index of this `FieldContent`. */ pragma[inline] override int getIndirectionIndex() { pragma[only_bind_into](result) = pragma[only_bind_out](indirectionIndex) From b87bf46c30199560e7245fc9f4650137d4c90c8c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 26 Jun 2023 11:28:32 +0100 Subject: [PATCH 28/93] C++: Fix joins. --- .../Security/CWE/CWE-193/InvalidPointerDeref.ql | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 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 4249f3a12b9..7b4f1775a07 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -131,8 +131,10 @@ module Barrier2 { } Instruction getABarrierInstruction(FlowState2 state) { - exists(IRGuardCondition g, ValueNumber value, boolean edge | - operandGuardChecks(g, value.getAUse(), _, state, edge) and + exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge | + use = value.getAUse() and + operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, + pragma[only_bind_into](state), pragma[only_bind_into](edge)) and result = value.getAnInstruction() and g.controls(result.getBlock(), edge) ) @@ -239,11 +241,13 @@ pragma[nomagic] predicate pointerAddInstructionHasBounds( PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta ) { - exists(Instruction right | + exists(Instruction right, Instruction instr2 | pai.getRight() = right and pai.getLeft() = sink1.asInstruction() and - bounded1(right, sink2.asInstruction(), delta) and - not [right, sink2.asInstruction()] = Barrier2::getABarrierInstruction(delta) + instr2 = sink2.asInstruction() and + bounded1(right, instr2, delta) and + not right = Barrier2::getABarrierInstruction(delta) and + not instr2 = Barrier2::getABarrierInstruction(delta) ) } From 527b537fee11ba879b10ced8b5215a81628598d1 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema <93738568+jketema@users.noreply.github.com> Date: Mon, 26 Jun 2023 12:57:43 +0200 Subject: [PATCH 29/93] Apply suggestions from code review Co-authored-by: Mathias Vorreiter Pedersen --- cpp/ql/test/library-tests/dataflow/fields/clearning.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp index cdcc0c7afac..0347c6725d2 100644 --- a/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp +++ b/cpp/ql/test/library-tests/dataflow/fields/clearning.cpp @@ -17,14 +17,14 @@ void test() S s; **s.x = user_input(); *s.x = 0; - sink(**s.x); // $ clean, as *s.x was overwritten and that contains the tainted **s.x + sink(**s.x); // clean, as *s.x was overwritten and that contains the tainted **s.x } { S s; **s.x = user_input(); **s.x = 0; - sink(**s.x); // $ clean, as **s.x was overwritten and tainted + sink(**s.x); // clean, as **s.x was overwritten and tainted } { From ef9d910a0704a626a939b0170d0bc6d536bd6158 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 26 Jun 2023 15:28:30 +0200 Subject: [PATCH 30/93] Update ruby/ql/lib/codeql/ruby/ApiGraphs.qll Co-authored-by: Erik Krogh Kristensen --- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index aaa80c3c205..71b7ddda9fd 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -108,7 +108,7 @@ module API { * * ### Backward data flow and classes * - * When inspecting the arguments of call, and the value flowing into that argument is a user-defined class (or an instance thereof), + * When inspecting the arguments of a call, and the value flowing into that argument is a user-defined class (or an instance thereof), * uses of `getMethod` will find method definitions in that class (including inherited ones) rather than finding method calls. * * This example illustrates how this can be used to model cases where the library calls a specific named method on a user-defined class: From b61e823cab82cd21929079b32fb3fb2901cf4e07 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 26 Jun 2023 15:31:18 +0200 Subject: [PATCH 31/93] Ruby: clarify qldoc for getADescendentModule --- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 71b7ddda9fd..f6c130eaa1c 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -253,7 +253,7 @@ module API { deprecated DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } /** - * Gets a module that descends from the value referenced by this API node. + * Gets a module or class that descends from the module or class referenced by this API node. */ bindingset[this] pragma[inline_late] From f6e244995a0492bd5b4c4fc150e8a8b70e99aeee Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 26 Jun 2023 15:32:11 +0200 Subject: [PATCH 32/93] Update ruby/ql/lib/codeql/ruby/ApiGraphs.qll Co-authored-by: Erik Krogh Kristensen --- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index f6c130eaa1c..2dd070ee0df 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -298,7 +298,7 @@ module API { } /** - * Gets an access to a constant with this valeu as the base of the access. + * Gets an access to a constant with this value as the base of the access. * * This is equivalent to `getMember(_)` but can be more efficient. */ From 8a43fc81ee3406b8e9a4bc4dabf436928c51f97a Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Mon, 26 Jun 2023 15:52:52 +0100 Subject: [PATCH 33/93] Java: Tweak some android tests They were all failing for me like: [autobuild] /home/ian/code/dev/target/codeql-java-integration-tests/ql/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/src/main/AndroidManifest.xml:5: Error: Main must extend android.app.Activity [Instantiatable] [autobuild] [autobuild] ~~~~ [autobuild] Explanation for issues of type "Instantiatable": [autobuild] Activities, services, broadcast receivers etc. registered in the manifest [autobuild] file (or for custom views, in a layout file) must be "instantiatable" by [autobuild] the system, which means that the class must be public, it must have an [autobuild] empty public constructor, and if it's an inner class, it must be a static [autobuild] inner class. I'm not sure why it works on CI but not locally, but either way this works around the issue. --- .../project/build.gradle.kts | 3 +++ .../test.expected | 1 + .../project/build.gradle.kts | 3 +++ .../test.expected | 1 + .../android-sample-old-style-no-wrapper/project/build.gradle | 4 ++++ .../java/android-sample-old-style-no-wrapper/test.expected | 1 + .../java/android-sample-old-style/project/build.gradle | 4 ++++ .../all-platforms/java/android-sample-old-style/test.expected | 1 + 8 files changed, 18 insertions(+) diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts index 95b77607cb3..75f63e5d4c4 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/project/build.gradle.kts @@ -54,6 +54,9 @@ android { versionName = "1.0" } + lintOptions { + disable("Instantiatable") + } } androidComponents { diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script-no-wrapper/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts index 95b77607cb3..75f63e5d4c4 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/project/build.gradle.kts @@ -54,6 +54,9 @@ android { versionName = "1.0" } + lintOptions { + disable("Instantiatable") + } } androidComponents { diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-kotlin-build-script/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle index 7751b92ae54..4d52801f178 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/project/build.gradle @@ -55,4 +55,8 @@ android { } variantFilter { variant -> if (variant.buildType.name == "debug") { setIgnore(true) } } + + lintOptions { + disable "Instantiatable" + } } diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style-no-wrapper/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle index 7751b92ae54..4d52801f178 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/project/build.gradle @@ -55,4 +55,8 @@ android { } variantFilter { variant -> if (variant.buildType.name == "debug") { setIgnore(true) } } + + lintOptions { + disable "Instantiatable" + } } diff --git a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected index 5ea6cd8c5ca..94266bb00a1 100644 --- a/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected +++ b/java/ql/integration-tests/all-platforms/java/android-sample-old-style/test.expected @@ -13,6 +13,7 @@ xmlFiles | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseJniLibFolders/merger.xml | | project/build/intermediates/incremental/mergeReleaseResources/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseResources/merger.xml | | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml:0:0:0:0 | project/build/intermediates/incremental/mergeReleaseShaders/merger.xml | +| project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml:0:0:0:0 | project/build/intermediates/lint_vital_partial_results/release/out/lint-issues-release.xml | | project/build/intermediates/merged_manifest/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifest/release/AndroidManifest.xml | | project/build/intermediates/merged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/merged_manifests/release/AndroidManifest.xml | | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml:0:0:0:0 | project/build/intermediates/packaged_manifests/release/AndroidManifest.xml | From 54632cd474e035524723e165a9a7ddee6a58102f Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Mon, 26 Jun 2023 20:04:14 +0200 Subject: [PATCH 34/93] C++: Replace `not exists` by `forex` in `clearsContent` --- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 85a4a224e52..838a660432f 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -785,11 +785,10 @@ predicate clearsContent(Node n, Content c) { p.getAnOperand() = op.getUse().getAst() ) or - not exists(PostUpdateNode pun, Content d | - d.impliesClearOf(c) and - storeStepImpl(_, d, pun, true) and - c.getIndirectionIndex() > d.getIndirectionIndex() and - pun.getPreUpdateNode() = n + forex(PostUpdateNode pun, Content d | + d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) and pun.getPreUpdateNode() = n + | + c.getIndirectionIndex() = d.getIndirectionIndex() ) ) } From ef383a135d506a4a996ec486f126d8ed84b09c4c Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Mon, 26 Jun 2023 19:09:43 +0100 Subject: [PATCH 35/93] C++: Prune the set of interesting pointer-arithmetic instructions by another flow. --- .../CWE/CWE-193/InvalidPointerDeref.ql | 24 +++++++++++++++++++ 1 file changed, 24 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 7b4f1775a07..6659b820659 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -241,6 +241,7 @@ pragma[nomagic] predicate pointerAddInstructionHasBounds( PointerAddInstruction pai, DataFlow::Node sink1, DataFlow::Node sink2, int delta ) { + InterestingPointerAddInstruction::isInteresting(pragma[only_bind_into](pai)) and exists(Instruction right, Instruction instr2 | pai.getRight() = right and pai.getLeft() = sink1.asInstruction() and @@ -251,6 +252,29 @@ predicate pointerAddInstructionHasBounds( ) } +module InterestingPointerAddInstruction { + private module PointerAddInstructionConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for the second + // projection in the `AllocToInvalidPointerConfig` module. + hasSize(source.asConvertedExpr(), _, _) + } + + predicate isSink(DataFlow::Node sink) { + sink.asInstruction() = any(PointerAddInstruction pai).getLeft() + } + } + + private import DataFlow::Global + + predicate isInteresting(PointerAddInstruction pai) { + exists(DataFlow::Node n | + n.asInstruction() = pai.getLeft() and + flowTo(n) + ) + } +} + /** * Holds if `pai` is non-strictly upper bounded by `sink2 + delta` and `sink1` is the * left operand of the pointer-arithmetic operation. From d42f6a08be2392c93d7b1a03a55ceeafbda59ace Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 09:26:48 +0200 Subject: [PATCH 36/93] Document deprecated imports --- docs/codeql/ql-language-reference/annotations.rst | 2 +- docs/codeql/ql-language-reference/modules.rst | 7 ++++++- .../ql-language-reference/ql-language-specification.rst | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/codeql/ql-language-reference/annotations.rst b/docs/codeql/ql-language-reference/annotations.rst index 70e4321667f..c87b479857e 100644 --- a/docs/codeql/ql-language-reference/annotations.rst +++ b/docs/codeql/ql-language-reference/annotations.rst @@ -126,7 +126,7 @@ body must also be annotated with ``cached``, otherwise a compiler error is repor ``deprecated`` ============== -**Available for**: |classes|, |algebraic datatypes|, |member predicates|, |non-member predicates|, |fields|, |modules|, |aliases| +**Available for**: |classes|, |algebraic datatypes|, |member predicates|, |non-member predicates|, |imports|, |fields|, |modules|, |aliases| The ``deprecated`` annotation is applied to names that are outdated and scheduled for removal in a future release of QL. diff --git a/docs/codeql/ql-language-reference/modules.rst b/docs/codeql/ql-language-reference/modules.rst index 42344c72e3d..ec4097ae019 100644 --- a/docs/codeql/ql-language-reference/modules.rst +++ b/docs/codeql/ql-language-reference/modules.rst @@ -264,7 +264,12 @@ Import statements are used for importing modules. They are of the form: Import statements are usually listed at the beginning of the module. Each import statement imports one module. You can import multiple modules by including multiple import statements (one for each module you want to import). -An import statement can also be :ref:`annotated ` with ``private``. + +An import statement can also be :ref:`annotated ` with +``private`` or ``deprecated``. If an import statement is annotated with +``private`` then the imported names are not reexported. If an imported name is +only reachable through deprecated imports in a given context then usage of the +name in that context will generate deprecation warnings. You can import a module under a different name using the ``as`` keyword, for example ``import javascript as js``. diff --git a/docs/codeql/ql-language-reference/ql-language-specification.rst b/docs/codeql/ql-language-reference/ql-language-specification.rst index e1e2dad10fa..9328a0f3ec3 100644 --- a/docs/codeql/ql-language-reference/ql-language-specification.rst +++ b/docs/codeql/ql-language-reference/ql-language-specification.rst @@ -794,7 +794,7 @@ The following table summarizes the syntactic constructs which can be marked with +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ | ``private`` | yes | | yes | yes | yes | yes | yes | yes | yes | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ -| ``deprecated`` | yes | | yes | yes | | yes | yes | yes | yes | +| ``deprecated`` | yes | | yes | yes | yes | yes | yes | yes | yes | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ | ``override`` | | | yes | | | yes | | | | +----------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+------------+ From 160771e673369f31139afef0b21efb60a378f92b Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 23 Jun 2023 13:27:10 +0200 Subject: [PATCH 37/93] C#: Avoid combinatorial explosions in GVN construction for types --- .../consistency-queries/TypeConsistency.qll | 13 ++++++ .../lib/semmle/code/csharp/AnnotatedType.qll | 2 + csharp/ql/lib/semmle/code/csharp/Generics.qll | 19 +++++---- csharp/ql/lib/semmle/code/csharp/Type.qll | 17 ++++---- csharp/ql/lib/semmle/code/csharp/TypeRef.qll | 2 +- .../ql/lib/semmle/code/csharp/Unification.qll | 42 +++++++++++-------- csharp/ql/lib/semmle/code/dotnet/Generics.qll | 2 +- 7 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 csharp/ql/consistency-queries/TypeConsistency.qll diff --git a/csharp/ql/consistency-queries/TypeConsistency.qll b/csharp/ql/consistency-queries/TypeConsistency.qll new file mode 100644 index 00000000000..37504a2df3f --- /dev/null +++ b/csharp/ql/consistency-queries/TypeConsistency.qll @@ -0,0 +1,13 @@ +import csharp +import semmle.code.csharp.Unification + +query predicate missingGvn(Type t, string cls) { + not exists(Gvn::getGlobalValueNumber(t)) and + cls = t.getPrimaryQlClasses() +} + +query predicate multipleGvn(Type t, Gvn::GvnType g, string cls) { + g = Gvn::getGlobalValueNumber(t) and + strictcount(Gvn::getGlobalValueNumber(t)) > 1 and + cls = t.getPrimaryQlClasses() +} diff --git a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll index 57221e47aa9..83bffc9b2a8 100644 --- a/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll +++ b/csharp/ql/lib/semmle/code/csharp/AnnotatedType.qll @@ -401,6 +401,8 @@ class AnnotatedArrayType extends AnnotatedType { class AnnotatedConstructedType extends AnnotatedType { override ConstructedType type; + AnnotatedConstructedType() { not type instanceof NullableType } + /** Gets the `i`th type argument of this constructed type. */ AnnotatedType getTypeArgument(int i) { result.getType() = type.getTypeArgument(i) and diff --git a/csharp/ql/lib/semmle/code/csharp/Generics.qll b/csharp/ql/lib/semmle/code/csharp/Generics.qll index ee850f25ea3..51c1dbc19fd 100644 --- a/csharp/ql/lib/semmle/code/csharp/Generics.qll +++ b/csharp/ql/lib/semmle/code/csharp/Generics.qll @@ -26,7 +26,8 @@ private import TypeRef class Generic extends DotNet::Generic, Declaration, @generic { Generic() { type_parameters(_, _, this, _) or - type_arguments(_, _, this) + type_arguments(_, _, this) or + nullable_underlying_type(this, _) } } @@ -39,7 +40,7 @@ class Generic extends DotNet::Generic, Declaration, @generic { class UnboundGeneric extends DotNet::UnboundGeneric, Generic { UnboundGeneric() { type_parameters(_, _, this, _) } - override TypeParameter getTypeParameter(int n) { type_parameters(result, n, this, _) } + final override TypeParameter getTypeParameter(int n) { type_parameters(result, n, this, _) } override ConstructedGeneric getAConstructedGeneric() { result.getUnboundGeneric() = this } @@ -67,7 +68,11 @@ private string getTypeParameterCommas(UnboundGeneric ug) { * generic method (`ConstructedMethod`). */ class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic { - ConstructedGeneric() { type_arguments(_, _, this) } + ConstructedGeneric() { + type_arguments(_, _, this) + or + nullable_underlying_type(this, _) + } override UnboundGeneric getUnboundGeneric() { constructed_generic(this, result) } @@ -75,8 +80,6 @@ class ConstructedGeneric extends DotNet::ConstructedGeneric, Generic { result = this.getUnboundGeneric().getUnboundDeclaration() } - override int getNumberOfTypeArguments() { result = count(int i | type_arguments(_, i, this)) } - override Type getTypeArgument(int i) { none() } override Type getATypeArgument() { result = this.getTypeArgument(_) } @@ -410,13 +413,13 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { override Location getALocation() { result = this.getUnboundDeclaration().getALocation() } - override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, getTypeRef(this)) } + override Type getTypeArgument(int n) { type_arguments(getTypeRef(result), n, this) } override UnboundGenericType getUnboundGeneric() { constructed_generic(this, getTypeRef(result)) } final override Type getChild(int n) { result = this.getTypeArgument(n) } - final override string toStringWithTypes() { + override string toStringWithTypes() { result = this.getUndecoratedName() + "<" + getTypeArgumentsToString(this) + ">" } @@ -424,7 +427,7 @@ class ConstructedType extends ValueOrRefType, ConstructedGeneric { result = this.getUndecoratedName() + "<" + getTypeArgumentsNames(this) + ">" } - final override predicate hasQualifiedName(string qualifier, string name) { + override predicate hasQualifiedName(string qualifier, string name) { exists(string name0 | name = name0 + "<" + getTypeArgumentsQualifiedNames(this) + ">" | exists(string enclosing | this.getDeclaringType().hasQualifiedName(qualifier, enclosing) and diff --git a/csharp/ql/lib/semmle/code/csharp/Type.qll b/csharp/ql/lib/semmle/code/csharp/Type.qll index 85fde20e07d..0b1e90fa7d6 100644 --- a/csharp/ql/lib/semmle/code/csharp/Type.qll +++ b/csharp/ql/lib/semmle/code/csharp/Type.qll @@ -974,29 +974,27 @@ class NullType extends RefType, @null_type { /** * A nullable type, for example `int?`. */ -class NullableType extends ValueType, DotNet::ConstructedGeneric, @nullable_type { +class NullableType extends ValueType, ConstructedType, @nullable_type { /** * Gets the underlying value type of this nullable type. * For example `int` in `int?`. */ Type getUnderlyingType() { nullable_underlying_type(this, getTypeRef(result)) } + override UnboundGenericStruct getUnboundGeneric() { + result.hasQualifiedName("System", "Nullable<>") + } + override string toStringWithTypes() { result = this.getUnderlyingType().toStringWithTypes() + "?" } - override Type getChild(int n) { result = this.getUnderlyingType() and n = 0 } - override Location getALocation() { result = this.getUnderlyingType().getALocation() } override Type getTypeArgument(int p) { p = 0 and result = this.getUnderlyingType() } override string getAPrimaryQlClass() { result = "NullableType" } - final override string getName() { - result = "Nullable<" + this.getUnderlyingType().getName() + ">" - } - final override predicate hasQualifiedName(string qualifier, string name) { qualifier = "System" and name = "Nullable<" + this.getUnderlyingType().getQualifiedName() + ">" @@ -1126,7 +1124,10 @@ class ArglistType extends Type, @arglist_type { * A type that could not be resolved. This could happen if an indirect reference * is not available at compilation time. */ -class UnknownType extends Type, @unknown_type { } +class UnknownType extends Type, @unknown_type { + /** Holds if this is the canonical unknown type, and not a type that failed to extract properly. */ + predicate isCanonical() { types(this, _, "") } +} /** * A type representing a tuple. For example, `(int, bool, string)`. diff --git a/csharp/ql/lib/semmle/code/csharp/TypeRef.qll b/csharp/ql/lib/semmle/code/csharp/TypeRef.qll index 7a6de44419f..f13168dd20d 100644 --- a/csharp/ql/lib/semmle/code/csharp/TypeRef.qll +++ b/csharp/ql/lib/semmle/code/csharp/TypeRef.qll @@ -16,7 +16,7 @@ private class TypeRef extends @typeref { typeref_type(this, result) or not typeref_type(this, _) and - result instanceof UnknownType + result.(UnknownType).isCanonical() } } diff --git a/csharp/ql/lib/semmle/code/csharp/Unification.qll b/csharp/ql/lib/semmle/code/csharp/Unification.qll index c3cbbd9ce97..7570070078f 100644 --- a/csharp/ql/lib/semmle/code/csharp/Unification.qll +++ b/csharp/ql/lib/semmle/code/csharp/Unification.qll @@ -15,9 +15,11 @@ module Gvn { * but only if the enclosing type is not a `GenericType`. */ string getNameNested(Type t) { - if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType - then result = t.getName() - else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + t.getName() + exists(string name | name = t.getName() | + if not t instanceof NestedType or t.(NestedType).getDeclaringType() instanceof GenericType + then result = name + else result = getNameNested(t.(NestedType).getDeclaringType()) + "+" + name + ) } /** @@ -47,8 +49,22 @@ module Gvn { not exists(this.getGenericDeclaringType()) and result = 0 } + /** + * Same as `getChild`, but safe-guards against potential extractor issues where + * multiple children exist at the same index, which may result in a combinatorial + * explosion. + */ + private Type getChildUnique(int i) { + result = unique(Type t | t = this.getChild(i) | t) + or + strictcount(this.getChild(i)) > 1 and + result.(UnknownType).isCanonical() + } + /** Gets the number of arguments of this type, not taking nested types into account. */ - int getNumberOfArgumentsSelf() { result = count(int i | exists(this.getChild(i)) and i >= 0) } + int getNumberOfArgumentsSelf() { + result = count(int i | exists(this.getChildUnique(i)) and i >= 0) + } /** Gets the number of arguments of this type, taking nested types into account. */ int getNumberOfArguments() { @@ -61,7 +77,7 @@ module Gvn { or exists(int offset | offset = this.getNumberOfDeclaringArguments() and - result = this.getChild(i - offset) and + result = this.getChildUnique(i - offset) and i >= offset ) } @@ -91,13 +107,9 @@ module Gvn { int getNumberOfTypeParameters() { this = TPointerTypeKind() and result = 1 or - this = TNullableTypeKind() and result = 1 - or this = TArrayTypeKind(_, _) and result = 1 or - exists(GenericType t | this = TConstructedType(t.getUnboundDeclaration()) | - result = t.getNumberOfArguments() - ) + exists(GenericType t | this = TConstructedType(t) | result = t.getNumberOfArguments()) } /** Gets the unbound declaration type that this kind corresponds to, if any. */ @@ -106,15 +118,12 @@ module Gvn { /** * Gets a textual representation of this kind when applied to arguments `args`. * - * This predicate is restricted to built-in generics (pointers, nullables, and - * arrays). + * This predicate is restricted to built-in generics (pointers and arrays). */ bindingset[args] string toStringBuiltin(string args) { this = TPointerTypeKind() and result = args + "*" or - this = TNullableTypeKind() and result = args + "?" - or exists(int rnk | this = TArrayTypeKind(_, rnk) | result = args + "[" + concat(int i | i in [0 .. rnk - 2] | ",") + "]" ) @@ -135,8 +144,6 @@ module Gvn { CompoundTypeKind getTypeKind(Type t) { result = TPointerTypeKind() and t instanceof PointerType or - result = TNullableTypeKind() and t instanceof NullableType - or t = any(ArrayType at | result = TArrayTypeKind(at.getDimension(), at.getRank())) or result = TConstructedType(t.getUnboundDeclaration()) @@ -280,6 +287,7 @@ module Gvn { pragma[noinline] private predicate toStringPart(int i, int j) { + this.isFullyConstructed() and exists(int offset | exists(GenericType t, int children | t = this.getConstructedGenericDeclaringTypeAt(i) and @@ -449,14 +457,12 @@ module Gvn { cached newtype TCompoundTypeKind = TPointerTypeKind() { Stages::UnificationStage::forceCachingInSameStage() } or - TNullableTypeKind() or TArrayTypeKind(int dim, int rnk) { exists(ArrayType at | dim = at.getDimension() and rnk = at.getRank()) } or TConstructedType(GenericType unboundDecl) { unboundDecl = any(GenericType t).getUnboundDeclaration() and not unboundDecl instanceof PointerType and - not unboundDecl instanceof NullableType and not unboundDecl instanceof ArrayType and not unboundDecl instanceof TupleType } diff --git a/csharp/ql/lib/semmle/code/dotnet/Generics.qll b/csharp/ql/lib/semmle/code/dotnet/Generics.qll index f84718d4b82..67b8fb2f5d0 100644 --- a/csharp/ql/lib/semmle/code/dotnet/Generics.qll +++ b/csharp/ql/lib/semmle/code/dotnet/Generics.qll @@ -41,7 +41,7 @@ abstract class ConstructedGeneric extends Generic { UnboundGeneric getUnboundGeneric() { none() } /** Gets the total number of type arguments. */ - int getNumberOfTypeArguments() { result = count(int i | exists(this.getTypeArgument(i))) } + final int getNumberOfTypeArguments() { result = count(int i | exists(this.getTypeArgument(i))) } } /** From 6869f03ccafb04c75bfbd07a667cc77a7238e79b Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 11:27:16 +0200 Subject: [PATCH 38/93] C#: Enable implicit this warnings for remaining packs --- csharp/downgrades/qlpack.yml | 1 + csharp/ql/campaigns/Solorigate/lib/qlpack.yml | 1 + csharp/ql/campaigns/Solorigate/src/qlpack.yml | 1 + csharp/ql/campaigns/Solorigate/test/qlpack.yml | 1 + csharp/ql/consistency-queries/qlpack.yml | 1 + csharp/ql/examples/qlpack.yml | 1 + csharp/ql/integration-tests/qlpack.yml | 1 + 7 files changed, 7 insertions(+) diff --git a/csharp/downgrades/qlpack.yml b/csharp/downgrades/qlpack.yml index c326f44bb0d..2ffd6b94f29 100644 --- a/csharp/downgrades/qlpack.yml +++ b/csharp/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/csharp-downgrades groups: csharp downgrades: . library: true +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml index 5f8c63b8ea3..8d19bca1d61 100644 --- a/csharp/ql/campaigns/Solorigate/lib/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/lib/qlpack.yml @@ -6,3 +6,4 @@ groups: library: true dependencies: codeql/csharp-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/src/qlpack.yml b/csharp/ql/campaigns/Solorigate/src/qlpack.yml index 65153d150f7..6bcc2def6f8 100644 --- a/csharp/ql/campaigns/Solorigate/src/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/src/qlpack.yml @@ -7,3 +7,4 @@ defaultSuiteFile: codeql-suites/solorigate.qls dependencies: codeql/csharp-all: ${workspace} codeql/csharp-solorigate-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/campaigns/Solorigate/test/qlpack.yml b/csharp/ql/campaigns/Solorigate/test/qlpack.yml index 7093935d651..dfd335f2726 100644 --- a/csharp/ql/campaigns/Solorigate/test/qlpack.yml +++ b/csharp/ql/campaigns/Solorigate/test/qlpack.yml @@ -10,3 +10,4 @@ dependencies: codeql/csharp-solorigate-queries: ${workspace} extractor: csharp tests: . +warnOnImplicitThis: true diff --git a/csharp/ql/consistency-queries/qlpack.yml b/csharp/ql/consistency-queries/qlpack.yml index 0d36d5156f5..8afefd18259 100644 --- a/csharp/ql/consistency-queries/qlpack.yml +++ b/csharp/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [csharp, test, consistency-queries] dependencies: codeql/csharp-all: ${workspace} extractor: csharp +warnOnImplicitThis: true diff --git a/csharp/ql/examples/qlpack.yml b/csharp/ql/examples/qlpack.yml index ca5ba5a3ab4..8796b678c16 100644 --- a/csharp/ql/examples/qlpack.yml +++ b/csharp/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/csharp-all: ${workspace} +warnOnImplicitThis: true diff --git a/csharp/ql/integration-tests/qlpack.yml b/csharp/ql/integration-tests/qlpack.yml index 972ad51f2cf..e66c1239b04 100644 --- a/csharp/ql/integration-tests/qlpack.yml +++ b/csharp/ql/integration-tests/qlpack.yml @@ -1,2 +1,3 @@ dependencies: codeql/csharp-all: '*' +warnOnImplicitThis: true From ab797065abc78e48bc2b4dfad1ea50fe3ce449d0 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 11:49:37 +0200 Subject: [PATCH 39/93] Go: Enable implicit this warnings for remaining packs --- go/downgrades/qlpack.yml | 1 + go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml | 1 + go/ql/config/legacy-support/qlpack.yml | 1 + go/ql/examples/qlpack.yml | 1 + go/ql/integration-tests/all-platforms/go/qlpack.yml | 1 + go/ql/integration-tests/linux-only/go/qlpack.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/go/downgrades/qlpack.yml b/go/downgrades/qlpack.yml index d3e056bea64..7e8e0022eb0 100644 --- a/go/downgrades/qlpack.yml +++ b/go/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/go-downgrades groups: go downgrades: . library: true +warnOnImplicitThis: true diff --git a/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml b/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml index ca0a6732f5a..7c42cb6974c 100644 --- a/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml +++ b/go/external-packs/codeql/suite-helpers/0.0.2/qlpack.yml @@ -1,3 +1,4 @@ name: codeql/suite-helpers version: 0.0.2 library: true +warnOnImplicitThis: true diff --git a/go/ql/config/legacy-support/qlpack.yml b/go/ql/config/legacy-support/qlpack.yml index bf3ee4d72a6..23ba44acd80 100644 --- a/go/ql/config/legacy-support/qlpack.yml +++ b/go/ql/config/legacy-support/qlpack.yml @@ -2,3 +2,4 @@ name: legacy-libraries-go version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: codeql-go +warnOnImplicitThis: true diff --git a/go/ql/examples/qlpack.yml b/go/ql/examples/qlpack.yml index 1f2ca2cd40a..006eb3c17b3 100644 --- a/go/ql/examples/qlpack.yml +++ b/go/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/go-all: ${workspace} +warnOnImplicitThis: true diff --git a/go/ql/integration-tests/all-platforms/go/qlpack.yml b/go/ql/integration-tests/all-platforms/go/qlpack.yml index 3a018bff52a..2fc2dd566dd 100644 --- a/go/ql/integration-tests/all-platforms/go/qlpack.yml +++ b/go/ql/integration-tests/all-platforms/go/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/go-all: '*' codeql/go-tests: '*' codeql/go-queries: '*' +warnOnImplicitThis: true diff --git a/go/ql/integration-tests/linux-only/go/qlpack.yml b/go/ql/integration-tests/linux-only/go/qlpack.yml index 3a018bff52a..2fc2dd566dd 100644 --- a/go/ql/integration-tests/linux-only/go/qlpack.yml +++ b/go/ql/integration-tests/linux-only/go/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/go-all: '*' codeql/go-tests: '*' codeql/go-queries: '*' +warnOnImplicitThis: true From 7fcdefbe70b6b85f16dc51e1ba6e343ba08e9a1c Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 11:54:13 +0200 Subject: [PATCH 40/93] Java: Enable implicit this warnings for remaining packs --- java/downgrades/qlpack.yml | 1 + java/ql/consistency-queries/qlpack.yml | 1 + java/ql/examples/qlpack.yml | 1 + java/ql/integration-tests/all-platforms/java/qlpack.yml | 1 + .../all-platforms/kotlin/annotation-id-consistency/qlpack.yml | 1 + .../all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml | 1 + .../kotlin/gradle_kotlinx_serialization/qlpack.yml | 1 + .../kotlin/kotlin-interface-inherited-default/qlpack.yml | 1 + .../all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml | 1 + java/ql/integration-tests/all-platforms/kotlin/qlpack.yml | 1 + .../linux-only/kotlin/custom_plugin/qlpack.yml | 1 + java/ql/integration-tests/linux-only/kotlin/qlpack.yml | 1 + java/ql/integration-tests/posix-only/kotlin/qlpack.yml | 2 +- 13 files changed, 13 insertions(+), 1 deletion(-) diff --git a/java/downgrades/qlpack.yml b/java/downgrades/qlpack.yml index b5a0f1bf411..6489ad2d9f6 100644 --- a/java/downgrades/qlpack.yml +++ b/java/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/java-downgrades groups: java downgrades: . library: true +warnOnImplicitThis: true diff --git a/java/ql/consistency-queries/qlpack.yml b/java/ql/consistency-queries/qlpack.yml index e5ab40251bb..1501c7fa736 100644 --- a/java/ql/consistency-queries/qlpack.yml +++ b/java/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql-java-consistency-queries version: 0.0.0 dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/examples/qlpack.yml b/java/ql/examples/qlpack.yml index 25699c305b0..bbd44274625 100644 --- a/java/ql/examples/qlpack.yml +++ b/java/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/java-all: ${workspace} +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/java/qlpack.yml b/java/ql/integration-tests/all-platforms/java/qlpack.yml index 9ead02fc564..4994af85a75 100644 --- a/java/ql/integration-tests/all-platforms/java/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/java/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml index 74fbac535d7..eeaa0e9f1b7 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/annotation-id-consistency/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/java-queries: '*' dataExtensions: ext/*.model.yml +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml index f1e981e8791..61fd32f9eb9 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/default-parameter-mad-flow/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/java-queries: '*' dataExtensions: ext/*.model.yml +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml index 8b18f2ea94a..d9848346b6f 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/gradle_kotlinx_serialization/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml index 814d1059ed5..69d3f0b3658 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/kotlin-interface-inherited-default/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml index ecc3ee3e4ff..1902aa3a68f 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/kotlin_java_static_fields/qlpack.yml @@ -3,3 +3,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml b/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml index 9ead02fc564..4994af85a75 100644 --- a/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml +++ b/java/ql/integration-tests/all-platforms/kotlin/qlpack.yml @@ -2,3 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml b/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml index e2f6b6de7ba..18ab2b37444 100644 --- a/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml +++ b/java/ql/integration-tests/linux-only/kotlin/custom_plugin/qlpack.yml @@ -1,3 +1,4 @@ name: integrationtest-custom-plugin dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/linux-only/kotlin/qlpack.yml b/java/ql/integration-tests/linux-only/kotlin/qlpack.yml index a1e82f7365a..b2ae6491ab8 100644 --- a/java/ql/integration-tests/linux-only/kotlin/qlpack.yml +++ b/java/ql/integration-tests/linux-only/kotlin/qlpack.yml @@ -1,2 +1,3 @@ dependencies: codeql/java-all: '*' +warnOnImplicitThis: true diff --git a/java/ql/integration-tests/posix-only/kotlin/qlpack.yml b/java/ql/integration-tests/posix-only/kotlin/qlpack.yml index 0c0975df53f..4994af85a75 100644 --- a/java/ql/integration-tests/posix-only/kotlin/qlpack.yml +++ b/java/ql/integration-tests/posix-only/kotlin/qlpack.yml @@ -2,4 +2,4 @@ dependencies: codeql/java-all: '*' codeql/java-tests: '*' codeql/java-queries: '*' - +warnOnImplicitThis: true From ab5e241310b39a386bfb1339de25e6998efe9454 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 11:56:29 +0200 Subject: [PATCH 41/93] Javascript: Enable implicit this warnings for remaining packs --- javascript/downgrades/qlpack.yml | 1 + javascript/ql/examples/qlpack.yml | 1 + javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml | 1 + .../ql/experimental/adaptivethreatmodeling/model/qlpack.yml | 1 + .../experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml | 1 + javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml | 1 + .../ql/experimental/adaptivethreatmodeling/test/qlpack.yml | 1 + javascript/ql/integration-tests/all-platforms/qlpack.yml | 1 + 8 files changed, 8 insertions(+) diff --git a/javascript/downgrades/qlpack.yml b/javascript/downgrades/qlpack.yml index b23d7602f30..2ef368fed0b 100644 --- a/javascript/downgrades/qlpack.yml +++ b/javascript/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/javascript-downgrades groups: javascript downgrades: . library: true +warnOnImplicitThis: true diff --git a/javascript/ql/examples/qlpack.yml b/javascript/ql/examples/qlpack.yml index 9bd7a0e73a9..75f3cbcb2cd 100644 --- a/javascript/ql/examples/qlpack.yml +++ b/javascript/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/javascript-all: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml index addde82c591..cda69768254 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/lib/qlpack.yml @@ -8,3 +8,4 @@ groups: - experimental dependencies: codeql/javascript-all: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml index e37547ed938..affda0e0b16 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/model/qlpack.yml @@ -6,3 +6,4 @@ groups: - experimental mlModels: - "resources/*.codeqlmodel" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml index a42eb4067ac..cdf0b9d5aab 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/modelbuilding/qlpack.yml @@ -8,3 +8,4 @@ groups: dependencies: codeql/javascript-experimental-atm-lib: ${workspace} codeql/javascript-experimental-atm-model: "0.3.1-2023-03-01-12h42m43s.strong-turtle-1xp3dqvv.ecb17d40286d14132b481c065a43459a7f0ba9059015b7a49c909c9f9ce5fec5" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml index dec06084ef1..6c2930991e4 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/src/qlpack.yml @@ -10,3 +10,4 @@ groups: dependencies: codeql/javascript-experimental-atm-lib: ${workspace} codeql/javascript-experimental-atm-model: "0.3.1-2023-03-01-12h42m43s.strong-turtle-1xp3dqvv.ecb17d40286d14132b481c065a43459a7f0ba9059015b7a49c909c9f9ce5fec5" +warnOnImplicitThis: true diff --git a/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml b/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml index 987b0ef55c4..5a7612bd33c 100644 --- a/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml +++ b/javascript/ql/experimental/adaptivethreatmodeling/test/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/javascript-experimental-atm-tests extractor: javascript dependencies: codeql/javascript-experimental-atm-model-building: ${workspace} +warnOnImplicitThis: true diff --git a/javascript/ql/integration-tests/all-platforms/qlpack.yml b/javascript/ql/integration-tests/all-platforms/qlpack.yml index f4bc24850b4..9f076584585 100644 --- a/javascript/ql/integration-tests/all-platforms/qlpack.yml +++ b/javascript/ql/integration-tests/all-platforms/qlpack.yml @@ -1,3 +1,4 @@ dependencies: codeql/javascript-all: '*' codeql/javascript-queries: '*' +warnOnImplicitThis: true From 2628552ef479503010ff57a4616b0d97a46f8ef3 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 27 Jun 2023 11:59:26 +0200 Subject: [PATCH 42/93] C++: Fix join-order problem in `clearsContent` --- .../semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 838a660432f..31915a888ac 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -786,7 +786,9 @@ predicate clearsContent(Node n, Content c) { ) or forex(PostUpdateNode pun, Content d | - d.impliesClearOf(c) and storeStepImpl(_, d, pun, true) and pun.getPreUpdateNode() = n + pragma[only_bind_into](d).impliesClearOf(pragma[only_bind_into](c)) and + storeStepImpl(_, d, pun, true) and + pun.getPreUpdateNode() = n | c.getIndirectionIndex() = d.getIndirectionIndex() ) From f41276cb7fee7bb858f7a717c03d638d1744afc6 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 12:00:13 +0200 Subject: [PATCH 43/93] Python: Enable implicit this warnings for remaining packs --- python/downgrades/qlpack.yml | 1 + python/ql/consistency-queries/qlpack.yml | 1 + python/ql/examples/qlpack.yml | 1 + python/tools/recorded-call-graph-metrics/ql/qlpack.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/python/downgrades/qlpack.yml b/python/downgrades/qlpack.yml index 12755ccb199..0c96e12e7cf 100644 --- a/python/downgrades/qlpack.yml +++ b/python/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/python-downgrades groups: python downgrades: . library: true +warnOnImplicitThis: true diff --git a/python/ql/consistency-queries/qlpack.yml b/python/ql/consistency-queries/qlpack.yml index c3433e9fcd6..f74d964dd91 100644 --- a/python/ql/consistency-queries/qlpack.yml +++ b/python/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [python, test, consistency-queries] dependencies: codeql/python-all: ${workspace} extractor: python +warnOnImplicitThis: true diff --git a/python/ql/examples/qlpack.yml b/python/ql/examples/qlpack.yml index 96edb7bd844..b3e268d26d6 100644 --- a/python/ql/examples/qlpack.yml +++ b/python/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/python-all: ${workspace} +warnOnImplicitThis: true diff --git a/python/tools/recorded-call-graph-metrics/ql/qlpack.yml b/python/tools/recorded-call-graph-metrics/ql/qlpack.yml index 3fee59d70bc..a08193635d5 100644 --- a/python/tools/recorded-call-graph-metrics/ql/qlpack.yml +++ b/python/tools/recorded-call-graph-metrics/ql/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.1 extractor: python dependencies: codeql/python-all: '*' +warnOnImplicitThis: true From c9cf0744c09654e756dba18ed4b0a76b05f9c49e Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 12:04:29 +0200 Subject: [PATCH 44/93] QL: Enable implicit this warnings for remaining packs --- ql/ql/consistency-queries/qlpack.yml | 1 + ql/ql/examples/qlpack.yml | 1 + ql/ql/test/callgraph/packs/lib/qlpack.yml | 3 ++- ql/ql/test/callgraph/packs/other/qlpack.yml | 1 + ql/ql/test/callgraph/packs/src/qlpack.yml | 3 ++- ql/ql/test/qlpack.yml | 1 + 6 files changed, 8 insertions(+), 2 deletions(-) diff --git a/ql/ql/consistency-queries/qlpack.yml b/ql/ql/consistency-queries/qlpack.yml index ffc5cdb7a1a..499d2130ef1 100644 --- a/ql/ql/consistency-queries/qlpack.yml +++ b/ql/ql/consistency-queries/qlpack.yml @@ -3,3 +3,4 @@ groups: [ql, test, consistency-queries] dependencies: codeql/ql: ${workspace} extractor: ql +warnOnImplicitThis: true diff --git a/ql/ql/examples/qlpack.yml b/ql/ql/examples/qlpack.yml index 0652ac37bd5..88fb9314a20 100644 --- a/ql/ql/examples/qlpack.yml +++ b/ql/ql/examples/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ql-examples groups: [ql, examples] dependencies: codeql/ql: ${workspace} +warnOnImplicitThis: true diff --git a/ql/ql/test/callgraph/packs/lib/qlpack.yml b/ql/ql/test/callgraph/packs/lib/qlpack.yml index 92e83d1e3d8..00c40b2431a 100644 --- a/ql/ql/test/callgraph/packs/lib/qlpack.yml +++ b/ql/ql/test/callgraph/packs/lib/qlpack.yml @@ -1,3 +1,4 @@ name: ql-testing-lib-pack version: 0.1.0 -extractor: ql-test-stuff \ No newline at end of file +extractor: ql-test-stuff +warnOnImplicitThis: true diff --git a/ql/ql/test/callgraph/packs/other/qlpack.yml b/ql/ql/test/callgraph/packs/other/qlpack.yml index d0b95cb68be..e0bc6a0a652 100644 --- a/ql/ql/test/callgraph/packs/other/qlpack.yml +++ b/ql/ql/test/callgraph/packs/other/qlpack.yml @@ -2,3 +2,4 @@ name: ql-other-pack-thing version: 0.1.0 dependencies: ql-testing-src-pack: '*' +warnOnImplicitThis: true diff --git a/ql/ql/test/callgraph/packs/src/qlpack.yml b/ql/ql/test/callgraph/packs/src/qlpack.yml index 2512d8f8483..c0d374d7cb4 100644 --- a/ql/ql/test/callgraph/packs/src/qlpack.yml +++ b/ql/ql/test/callgraph/packs/src/qlpack.yml @@ -1,4 +1,5 @@ name: ql-testing-src-pack version: 0.1.0 dependencies: - ql-testing-lib-pack: ${workspace} \ No newline at end of file + ql-testing-lib-pack: ${workspace} +warnOnImplicitThis: true diff --git a/ql/ql/test/qlpack.yml b/ql/ql/test/qlpack.yml index f31941cdf10..b16fd7e3e20 100644 --- a/ql/ql/test/qlpack.yml +++ b/ql/ql/test/qlpack.yml @@ -5,3 +5,4 @@ dependencies: codeql/ql-examples: ${workspace} extractor: ql tests: . +warnOnImplicitThis: true From 41c071ff741bde00f2d27450c0e290417a42d619 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 12:07:05 +0200 Subject: [PATCH 45/93] Ruby: Enable implicit this warnings for remaining packs --- ruby/downgrades/qlpack.yml | 1 + ruby/ql/consistency-queries/qlpack.yml | 1 + ruby/ql/examples/qlpack.yml | 1 + ruby/ql/integration-tests/all-platforms/qlpack.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/ruby/downgrades/qlpack.yml b/ruby/downgrades/qlpack.yml index c23e8cc44d6..c777eec353c 100644 --- a/ruby/downgrades/qlpack.yml +++ b/ruby/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ruby-downgrades groups: ruby downgrades: . library: true +warnOnImplicitThis: true diff --git a/ruby/ql/consistency-queries/qlpack.yml b/ruby/ql/consistency-queries/qlpack.yml index 707cada6cd2..9a1292eefa0 100644 --- a/ruby/ql/consistency-queries/qlpack.yml +++ b/ruby/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/ruby-consistency-queries groups: [ruby, test, consistency-queries] dependencies: codeql/ruby-all: ${workspace} +warnOnImplicitThis: true diff --git a/ruby/ql/examples/qlpack.yml b/ruby/ql/examples/qlpack.yml index fc159c65692..5d2cd48bbb9 100644 --- a/ruby/ql/examples/qlpack.yml +++ b/ruby/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/ruby-all: ${workspace} +warnOnImplicitThis: true diff --git a/ruby/ql/integration-tests/all-platforms/qlpack.yml b/ruby/ql/integration-tests/all-platforms/qlpack.yml index a27def4e4d7..5a103c573d4 100644 --- a/ruby/ql/integration-tests/all-platforms/qlpack.yml +++ b/ruby/ql/integration-tests/all-platforms/qlpack.yml @@ -1,3 +1,4 @@ dependencies: codeql/ruby-all: '*' codeql/ruby-queries: '*' +warnOnImplicitThis: true From 8ce09438a02dbbbce00a451a5756a49a9ad0a5fe Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 12:09:27 +0200 Subject: [PATCH 46/93] Swift: Enable implicit this warnings for remaining packs --- swift/downgrades/qlpack.yml | 1 + swift/integration-tests/qlpack.yml | 1 + swift/ql/consistency-queries/qlpack.yml | 1 + swift/ql/examples/qlpack.yml | 1 + 4 files changed, 4 insertions(+) diff --git a/swift/downgrades/qlpack.yml b/swift/downgrades/qlpack.yml index 3fc919124df..3de4eeebc7c 100644 --- a/swift/downgrades/qlpack.yml +++ b/swift/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/swift-downgrades groups: swift downgrades: . library: true +warnOnImplicitThis: true diff --git a/swift/integration-tests/qlpack.yml b/swift/integration-tests/qlpack.yml index c0030d14bdf..f0a64418576 100644 --- a/swift/integration-tests/qlpack.yml +++ b/swift/integration-tests/qlpack.yml @@ -4,3 +4,4 @@ dependencies: codeql/swift-all: ${workspace} tests: . extractor: swift +warnOnImplicitThis: true diff --git a/swift/ql/consistency-queries/qlpack.yml b/swift/ql/consistency-queries/qlpack.yml index 57ef2babccf..c1ee319c393 100644 --- a/swift/ql/consistency-queries/qlpack.yml +++ b/swift/ql/consistency-queries/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/swift-consistency-queries groups: [swift, test, consistency-queries] dependencies: codeql/swift-all: ${workspace} +warnOnImplicitThis: true diff --git a/swift/ql/examples/qlpack.yml b/swift/ql/examples/qlpack.yml index ed3c6f12bac..c29a8b7783d 100644 --- a/swift/ql/examples/qlpack.yml +++ b/swift/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/swift-all: ${workspace} +warnOnImplicitThis: true From 920270871984fbad0b90830f57931067c9f7e066 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 13:03:11 +0200 Subject: [PATCH 47/93] Misc: Enable implicit this warnings for remaining packs --- misc/legacy-support/cpp/qlpack.yml | 1 + misc/legacy-support/csharp/qlpack.yml | 1 + misc/legacy-support/java/qlpack.yml | 1 + misc/legacy-support/javascript/qlpack.yml | 1 + misc/legacy-support/python/qlpack.yml | 1 + misc/suite-helpers/qlpack.yml | 1 + 6 files changed, 6 insertions(+) diff --git a/misc/legacy-support/cpp/qlpack.yml b/misc/legacy-support/cpp/qlpack.yml index 658481ea8e0..f1da9f7b4da 100644 --- a/misc/legacy-support/cpp/qlpack.yml +++ b/misc/legacy-support/cpp/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: - codeql/cpp-all +warnOnImplicitThis: true diff --git a/misc/legacy-support/csharp/qlpack.yml b/misc/legacy-support/csharp/qlpack.yml index 7ad61e0e6cc..2def03b3565 100644 --- a/misc/legacy-support/csharp/qlpack.yml +++ b/misc/legacy-support/csharp/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: - codeql/csharp-all +warnOnImplicitThis: true diff --git a/misc/legacy-support/java/qlpack.yml b/misc/legacy-support/java/qlpack.yml index eec38e76143..112e5c09e0d 100644 --- a/misc/legacy-support/java/qlpack.yml +++ b/misc/legacy-support/java/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: - codeql/java-all +warnOnImplicitThis: true diff --git a/misc/legacy-support/javascript/qlpack.yml b/misc/legacy-support/javascript/qlpack.yml index 9bd1118ba34..b07af4d647d 100644 --- a/misc/legacy-support/javascript/qlpack.yml +++ b/misc/legacy-support/javascript/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: - codeql-javascript +warnOnImplicitThis: true diff --git a/misc/legacy-support/python/qlpack.yml b/misc/legacy-support/python/qlpack.yml index 6d7fa67d182..f2cbafa7448 100644 --- a/misc/legacy-support/python/qlpack.yml +++ b/misc/legacy-support/python/qlpack.yml @@ -3,3 +3,4 @@ version: 0.0.0 # Note libraryPathDependencies is obsolete and should not be used in new qlpacks. libraryPathDependencies: - codeql/python-all +warnOnImplicitThis: true diff --git a/misc/suite-helpers/qlpack.yml b/misc/suite-helpers/qlpack.yml index f07f050124a..bd19b10ebaf 100644 --- a/misc/suite-helpers/qlpack.yml +++ b/misc/suite-helpers/qlpack.yml @@ -1,3 +1,4 @@ name: codeql/suite-helpers version: 0.5.4-dev groups: shared +warnOnImplicitThis: true From b4ef2437333f828fceb077491233eb0893139642 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Mon, 26 Jun 2023 12:11:50 +0200 Subject: [PATCH 48/93] Add workflow to check for warnOnImplicitThis --- .github/workflows/check-implicit-this.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/check-implicit-this.yml diff --git a/.github/workflows/check-implicit-this.yml b/.github/workflows/check-implicit-this.yml new file mode 100644 index 00000000000..8539c7b3330 --- /dev/null +++ b/.github/workflows/check-implicit-this.yml @@ -0,0 +1,22 @@ +name: "Check implicit this warnings" + +on: workflow_dispatch + +jobs: + check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Check that implicit this warnings is enabled for all packs + shell: bash + run: | + EXIT_CODE=0 + packs="$(find . -iname 'qlpack.yml')" + for pack_file in ${packs}; do + option="$(yq '.warnOnImplicitThis' ${pack_file})" + if [ "${option}" != "true" ]; then + echo "warnOnImplicitThis property must be set to 'true' for pack ${pack_file}" + EXIT_CODE=1 + fi + done + exit "${EXIT_CODE}" From 51176bdff30c2b7dea5e223abc64d4712678fa1e Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 27 Jun 2023 12:59:22 +0100 Subject: [PATCH 49/93] C++: Add Geoffrey's testcases. --- .../ConstantSizeArrayOffByOne.expected | 49 +++++++++++++++++++ .../CWE/CWE-193/constant-size/test.cpp | 27 ++++++++++ 2 files changed, 76 insertions(+) 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 4f5031a0a6e..ff75c77e702 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 @@ -1,41 +1,74 @@ edges +| test.cpp:34:10:34:12 | buf | test.cpp:34:5:34:24 | access to array | | test.cpp:35:10:35:12 | buf | test.cpp:35:5:35:22 | access to array | | test.cpp:36:10:36:12 | buf | test.cpp:36:5:36:24 | access to array | +| test.cpp:39:14:39:16 | buf | test.cpp:39:9:39:19 | access to array | | test.cpp:43:14:43:16 | buf | test.cpp:43:9:43:19 | access to array | +| test.cpp:48:10:48:12 | buf | test.cpp:48:5:48:24 | access to array | | test.cpp:49:10:49:12 | buf | test.cpp:49:5:49:22 | access to array | | test.cpp:50:10:50:12 | buf | test.cpp:50:5:50:24 | access to array | +| test.cpp:53:14:53:16 | buf | test.cpp:53:9:53:19 | access to array | | test.cpp:57:14:57:16 | buf | test.cpp:57:9:57:19 | access to array | | test.cpp:61:14:61:16 | buf | test.cpp:61:9:61:19 | access to array | +| test.cpp:70:33:70:33 | p | test.cpp:71:5:71:17 | access to array | | test.cpp:70:33:70:33 | p | test.cpp:72:5:72:15 | access to array | +| test.cpp:76:26:76:46 | & ... | test.cpp:66:32:66:32 | p | +| test.cpp:76:32:76:34 | buf | test.cpp:76:26:76:46 | & ... | | test.cpp:77:26:77:44 | & ... | test.cpp:66:32:66:32 | p | | test.cpp:77:32:77:34 | buf | test.cpp:77:26:77:44 | & ... | | 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:85:34:85:36 | buf | test.cpp:87:5:87:31 | access to array | | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | access to array | +| test.cpp:96:13:96:15 | arr | test.cpp:96:13:96:18 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:111:17:111:19 | arr | test.cpp:119:17:119:22 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:115:35:115:37 | arr | test.cpp:119:17:119:22 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:111:17:111:22 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:115:35:115:40 | access to array | +| test.cpp:119:17:119:19 | arr | test.cpp:119:17:119:22 | access to array | | 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 | +| test.cpp:148:23:148:28 | buffer | test.cpp:150:5:150:11 | access to array | +| test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | +| test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array | +| test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | nodes +| test.cpp:34:5:34:24 | access to array | semmle.label | access to array | +| test.cpp:34:10:34:12 | buf | semmle.label | buf | | test.cpp:35:5:35:22 | access to array | semmle.label | access to array | | test.cpp:35:10:35:12 | buf | semmle.label | buf | | test.cpp:36:5:36:24 | access to array | semmle.label | access to array | | test.cpp:36:10:36:12 | buf | semmle.label | buf | +| test.cpp:39:9:39:19 | access to array | semmle.label | access to array | +| test.cpp:39:14:39:16 | buf | semmle.label | buf | | test.cpp:43:9:43:19 | access to array | semmle.label | access to array | | test.cpp:43:14:43:16 | buf | semmle.label | buf | +| test.cpp:48:5:48:24 | access to array | semmle.label | access to array | +| test.cpp:48:10:48:12 | buf | semmle.label | buf | | test.cpp:49:5:49:22 | access to array | semmle.label | access to array | | test.cpp:49:10:49:12 | buf | semmle.label | buf | | test.cpp:50:5:50:24 | access to array | semmle.label | access to array | | test.cpp:50:10:50:12 | buf | semmle.label | buf | +| test.cpp:53:9:53:19 | access to array | semmle.label | access to array | +| test.cpp:53:14:53:16 | buf | semmle.label | buf | | test.cpp:57:9:57:19 | access to array | semmle.label | access to array | | test.cpp:57:14:57:16 | buf | semmle.label | buf | | test.cpp:61:9:61:19 | access to array | semmle.label | access to array | | test.cpp:61:14:61:16 | buf | semmle.label | buf | | test.cpp:66:32:66:32 | p | semmle.label | p | +| test.cpp:66:32:66:32 | p | semmle.label | p | | test.cpp:70:33:70:33 | p | semmle.label | p | +| test.cpp:71:5:71:17 | access to array | semmle.label | access to array | | test.cpp:72:5:72:15 | access to array | semmle.label | access to array | +| test.cpp:76:26:76:46 | & ... | semmle.label | & ... | +| test.cpp:76:32:76:34 | buf | semmle.label | buf | | test.cpp:77:26:77:44 | & ... | semmle.label | & ... | | test.cpp:77:32:77:34 | buf | semmle.label | buf | | test.cpp:79:27:79:34 | buf | semmle.label | buf | @@ -43,6 +76,14 @@ nodes | test.cpp:85:34:85:36 | buf | semmle.label | buf | | test.cpp:87:5:87:31 | access to array | semmle.label | access to array | | test.cpp:88:5:88:27 | access to array | semmle.label | access to array | +| test.cpp:96:13:96:15 | arr | semmle.label | arr | +| test.cpp:96:13:96:18 | access to array | semmle.label | access to array | +| test.cpp:111:17:111:19 | arr | semmle.label | arr | +| test.cpp:111:17:111:22 | access to array | semmle.label | access to array | +| test.cpp:115:35:115:37 | arr | semmle.label | arr | +| test.cpp:115:35:115:40 | access to array | semmle.label | access to array | +| test.cpp:119:17:119:19 | arr | semmle.label | arr | +| test.cpp:119:17:119:22 | access to array | semmle.label | access to array | | 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 | @@ -50,6 +91,12 @@ nodes | 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 | +| test.cpp:148:23:148:28 | buffer | semmle.label | buffer | +| test.cpp:150:5:150:11 | access to array | semmle.label | access to array | +| test.cpp:151:5:151:11 | access to array | semmle.label | access to array | +| test.cpp:159:25:159:29 | array | semmle.label | array | +| test.cpp:161:5:161:10 | access to array | semmle.label | access to array | +| test.cpp:162:5:162:10 | access to array | semmle.label | access to array | 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 | @@ -64,3 +111,5 @@ subpaths | test.cpp:88:5:88:27 | PointerAdd: access to array | test.cpp:85:34:85:36 | buf | test.cpp:88:5:88:27 | 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:88:5:88:31 | 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 | +| test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write | +| test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write | 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 f799518f6ec..902bf5a2cd9 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 @@ -142,3 +142,30 @@ void testStrncmp1() { char asdf[5]; testStrncmp2(asdf); } + +void pointer_size_larger_than_array_element_size() { + unsigned char buffer[100]; // getByteSize() = 100 + int *ptr = (int *)buffer; // pai.getElementSize() will be sizeof(int) = 4 -> size = 25 + + ptr[24] = 0; // GOOD: writes bytes 96, 97, 98, 99 + ptr[25] = 0; // BAD: writes bytes 100, 101, 102, 103 +} + +struct vec2 { int x, y; }; +struct vec3 { int x, y, z; }; + +void pointer_size_smaller_than_array_element_size_but_does_not_divide_it() { + vec3 array[3]; // getByteSize() = 9 * sizeof(int) + vec2 *ptr = (vec2 *)array; // pai.getElementSize() will be 2 * sizeof(int) -> size = 4 + + ptr[3] = vec2{}; // GOOD: writes ints 6, 7 + ptr[4] = vec2{}; // BAD: writes ints 8, 9 +} + +void pointer_size_larger_than_array_element_size_and_does_not_divide_it() { + vec2 array[2]; // getByteSize() = 4 * sizeof(int) = 4 * 4 = 16 + vec3 *ptr = (vec3 *)array; // pai.getElementSize() will be 3 * sizeof(int) -> size = 1 + + ptr[0] = vec3{}; // GOOD: writes ints 0, 1, 2 + ptr[1] = vec3{}; // BAD: writes ints 3, 4, 5 [NOT DETECTED] +} From d588f522621662ed3574ed09d3438180af393d11 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Tue, 27 Jun 2023 13:33:52 +0100 Subject: [PATCH 50/93] Kotlin: Remove an out-of-date comment --- java/kotlin-extractor/src/main/kotlin/utils/Logger.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt index 3b66e527429..55624f3c342 100644 --- a/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt +++ b/java/kotlin-extractor/src/main/kotlin/utils/Logger.kt @@ -204,9 +204,6 @@ open class LoggerBase(val logCounter: LogCounter) { val severity = info.first val count = info.second if(count >= logCounter.diagnosticLimit) { - // We don't know if this location relates to an error - // or a warning, so we just declare hitting the limit - // to be an error regardless. val message = "Total of $count diagnostics (reached limit of ${logCounter.diagnosticLimit}) from $caller." if (verbosity >= 1) { emitDiagnostic(tw, severity, "Limit", message, message) From d1979197c75d2ea2d17f2d5eea41ea7a71719c05 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Tue, 27 Jun 2023 11:20:08 +0200 Subject: [PATCH 51/93] CPP: Enable implicit this warnings for remaining packs --- cpp/downgrades/qlpack.yml | 1 + cpp/ql/examples/qlpack.yml | 1 + .../query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/cpp/downgrades/qlpack.yml b/cpp/downgrades/qlpack.yml index 9155f820e7e..bcaae0a19c9 100644 --- a/cpp/downgrades/qlpack.yml +++ b/cpp/downgrades/qlpack.yml @@ -2,3 +2,4 @@ name: codeql/cpp-downgrades groups: cpp downgrades: . library: true +warnOnImplicitThis: true diff --git a/cpp/ql/examples/qlpack.yml b/cpp/ql/examples/qlpack.yml index d7f3a9634cc..2b92e450a8c 100644 --- a/cpp/ql/examples/qlpack.yml +++ b/cpp/ql/examples/qlpack.yml @@ -4,3 +4,4 @@ groups: - examples dependencies: codeql/cpp-all: ${workspace} +warnOnImplicitThis: true diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml index 096e9d13dd2..d29089ece4d 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-190/semmle/tainted/qlpack.yml @@ -6,3 +6,4 @@ dependencies: codeql/cpp-queries: ${workspace} extractor: cpp tests: . +warnOnImplicitThis: true From fcb2f1082c669635fd20e200b78d70d095ed306b Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 27 Jun 2023 15:23:55 +0200 Subject: [PATCH 52/93] Java: Fix external API name for nested types This fixes the name of reported external APIs for nested types. The `toString()` method of `getSourceDeclaration()` would report the name of a type, but not the name of the enclosing type. This results in missing information in the `UnsupportedExternalAPIs.ql` query. For example, previously it would report: ``` org.zapodot.junit.db.Builder#build() ``` However, the `Builder` class does not exist in the package and is only a nested type within `EmbeddedDatabaseRule`. The correct name should be: ``` org.zapodot.junit.db.EmbeddedDatabaseRule$Builder#build() ``` This name also matches the format of MaD. --- java/ql/src/Telemetry/ExternalApi.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index a8624f8fef6..04785a208a8 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -27,8 +27,8 @@ class ExternalApi extends Callable { */ string getApiName() { result = - this.getDeclaringType().getPackage() + "." + this.getDeclaringType().getSourceDeclaration() + - "#" + this.getName() + paramsString(this) + this.getDeclaringType().getPackage() + "." + this.getDeclaringType().nestedName() + "#" + + this.getName() + paramsString(this) } private string getJarName() { From 6812389fc87981c9ab7cddb52903973c17335dd2 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 27 Jun 2023 16:31:06 +0200 Subject: [PATCH 53/93] C#: Fix external API name for nested types This fixes the name of reported external APIs for nested types. The `getDeclaringType().getUnboundDeclaration()`'s `toString()` method reports the name of the type, but not the name of the declaring type. This results in missing information in the `UnsupportedExternalAPIs.ql` query. For example, previously it would report: ``` GitHub.Nested#NestedClass.Test() ``` However, the `NestedClass` class does not exist in the namespace and is only a nested type within `MyFirstClass`. The correct name should be: ``` GitHub.Nested#MyFirstClass+NestedClass.Test() ``` This name also matches the format of MaD. --- csharp/ql/src/Telemetry/ExternalApi.qll | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/csharp/ql/src/Telemetry/ExternalApi.qll b/csharp/ql/src/Telemetry/ExternalApi.qll index 9358e9ce8e4..0921bdf7b5c 100644 --- a/csharp/ql/src/Telemetry/ExternalApi.qll +++ b/csharp/ql/src/Telemetry/ExternalApi.qll @@ -50,7 +50,7 @@ class ExternalApi extends DotNet::Callable { bindingset[this] private string getSignature() { result = - this.getDeclaringType().getUnboundDeclaration() + "." + this.getName() + "(" + + nestedName(this.getDeclaringType().getUnboundDeclaration()) + "." + this.getName() + "(" + parameterQualifiedTypeNamesToString(this) + ")" } @@ -118,6 +118,21 @@ class ExternalApi extends DotNet::Callable { } } +/** + * Gets the nested name of the declaration. + * + * If the declaration is not a nested type, the result is the same as \`getName()\`. + * Otherwise the name of the nested type is prefixed with a \`+\` and appended to + * the name of the enclosing type, which might be a nested type as well. + */ +private string nestedName(Declaration declaration) { + not exists(declaration.getDeclaringType().getUnboundDeclaration()) and + result = declaration.getName() + or + nestedName(declaration.getDeclaringType().getUnboundDeclaration()) + "+" + declaration.getName() = + result +} + /** * Gets the limit for the number of results produced by a telemetry query. */ From af41dabc14223e154f708c1f7c01fd05b5fc8419 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Mon, 26 Jun 2023 16:13:39 +0200 Subject: [PATCH 54/93] Swift: extend the frontend observer --- swift/extractor/main.cpp | 3 ++- swift/third_party/load.bzl | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/swift/extractor/main.cpp b/swift/extractor/main.cpp index de7fd703161..4f1dd990be9 100644 --- a/swift/extractor/main.cpp +++ b/swift/extractor/main.cpp @@ -85,6 +85,7 @@ class Observer : public swift::FrontendObserver { void parsedArgs(swift::CompilerInvocation& invocation) override { auto& options = invocation.getFrontendOptions(); + options.KeepASTContext = true; lockOutputSwiftModuleTraps(state, options); processFrontendOptions(state, options); } @@ -93,7 +94,7 @@ class Observer : public swift::FrontendObserver { instance.addDiagnosticConsumer(&diagConsumer); } - void performedSemanticAnalysis(swift::CompilerInstance& compiler) override { + void performedCompilation(swift::CompilerInstance& compiler) override { codeql::extractSwiftFiles(state, compiler); codeql::extractSwiftInvocation(state, compiler, invocationTrap); codeql::extractExtractLazyDeclarations(state, compiler); diff --git a/swift/third_party/load.bzl b/swift/third_party/load.bzl index 545ac2b4b95..a046946e573 100644 --- a/swift/third_party/load.bzl +++ b/swift/third_party/load.bzl @@ -4,11 +4,11 @@ load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") # TODO: remove `remove-result-of.patch` once we update to a Swift version containing # https://github.com/apple/swift/commit/2ed2cea2 # (probably when updating to 5.9) -_swift_prebuilt_version = "swift-5.8.1-RELEASE.208" +_swift_prebuilt_version = "swift-5.8.1-RELEASE.210" _swift_sha_map = { - "Linux-X64": "1d93286d6219e5c5746938ab9287d90efea98039f022cb1433296ccbc1684bc0", - "macOS-ARM64": "a29ce5143cb2c68190e337a35ebb163e961a58b9d8826fe7f8daf4d8381ee75d", - "macOS-X64": "a7e63ea732750c783142083df20a34c8d337b9b9ba210fa6a9e5ada7b7880189", + "Linux-X64": "d8c715044c3989683b3f986a377647697245ed6bbdc2add13890433c9dc732a4", + "macOS-ARM64": "2ca169f299bce61034bd011f081a55be07a19f20fd7ea855b241f43fe0fb76d2", + "macOS-X64": "80e2e8cefbd78d71e54923c1da36cac3511b4c69c4841cf35bef383bc6750d18", } _swift_arch_map = { From ea5eda0f2249ff204bee1bdd9ee6b8dd5cbb0b86 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Tue, 27 Jun 2023 15:26:59 +0200 Subject: [PATCH 55/93] Swift: adjust test expectations --- .../CONSISTENCY/CfgConsistency.expected | 6 ---- .../CONSISTENCY/CfgConsistency.expected | 2 -- .../CONSISTENCY/CfgConsistency.expected | 6 ---- .../CONSISTENCY/CfgConsistency.expected | 2 -- .../ast/CONSISTENCY/CfgConsistency.expected | 9 ----- .../ql/test/library-tests/ast/Errors.expected | 4 --- .../test/library-tests/ast/PrintAst.expected | 36 ++++++++++++------- .../graph/CONSISTENCY/CfgConsistency.expected | 2 -- .../CONSISTENCY/CfgConsistency.expected | 5 --- .../CONSISTENCY/CfgConsistency.expected | 10 ------ .../CONSISTENCY/CfgConsistency.expected | 3 -- .../CONSISTENCY/CfgConsistency.expected | 2 -- .../CONSISTENCY/CfgConsistency.expected | 11 ------ .../CONSISTENCY/CfgConsistency.expected | 5 --- .../CONSISTENCY/CfgConsistency.expected | 2 -- .../CONSISTENCY/CfgConsistency.expected | 6 ---- .../CONSISTENCY/CfgConsistency.expected | 13 ------- .../CONSISTENCY/CfgConsistency.expected | 9 ----- .../CONSISTENCY/CfgConsistency.expected | 2 -- .../CONSISTENCY/CfgConsistency.expected | 5 --- .../CONSISTENCY/CfgConsistency.expected | 5 --- .../CONSISTENCY/CfgConsistency.expected | 5 --- .../CONSISTENCY/CfgConsistency.expected | 5 --- 23 files changed, 24 insertions(+), 131 deletions(-) delete mode 100644 swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected delete mode 100644 swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected diff --git a/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/extractor-tests/declarations/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -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 | ... = ... | diff --git a/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/extractor-tests/expressions/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/extractor-tests/generated/decl/EnumDecl/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -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 | ... = ... | diff --git a/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/extractor-tests/statements/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected index fdfaa9f18cd..9c49013832a 100644 --- a/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/ast/CONSISTENCY/CfgConsistency.expected @@ -8,13 +8,4 @@ multipleSuccessors deadEnd | cfg.swift:33:49:33:60 | call to isZero(x:) | | cfg.swift:144:18:144:34 | ... .&&(_:_:) ... | -| 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 | ... = ... | | patterns.swift:16:10:16:14 | =~ ... | diff --git a/swift/ql/test/library-tests/ast/Errors.expected b/swift/ql/test/library-tests/ast/Errors.expected index e289df1fc15..a65eb1b2466 100644 --- a/swift/ql/test/library-tests/ast/Errors.expected +++ b/swift/ql/test/library-tests/ast/Errors.expected @@ -1,5 +1 @@ -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | -| file://:0:0:0:0 | ... .combine(_:) | UnresolvedDotExpr | | patterns.swift:16:12:16:12 | OverloadedDeclRefExpr | OverloadedDeclRefExpr | diff --git a/swift/ql/test/library-tests/ast/PrintAst.expected b/swift/ql/test/library-tests/ast/PrintAst.expected index cb7091193f1..39320293cbc 100644 --- a/swift/ql/test/library-tests/ast/PrintAst.expected +++ b/swift/ql/test/library-tests/ast/PrintAst.expected @@ -2795,11 +2795,14 @@ cfg.swift: #-----| getSource(): [IntegerLiteralExpr] 1 #-----| getLabel(0): [CaseLabelItem] .B #-----| getPattern(): [EnumElementPattern] .B -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(7): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -3482,11 +3485,14 @@ declarations.swift: #-----| getSource(): [IntegerLiteralExpr] 4 #-----| getLabel(0): [CaseLabelItem] .value5 #-----| getPattern(): [EnumElementPattern] .value5 -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(10): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -4394,11 +4400,14 @@ expressions.swift: #-----| getSource(): [IntegerLiteralExpr] 0 #-----| getLabel(0): [CaseLabelItem] .failed #-----| getPattern(): [EnumElementPattern] .failed -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(5): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get @@ -6656,11 +6665,14 @@ statements.swift: #-----| getSource(): [IntegerLiteralExpr] 0 #-----| getLabel(0): [CaseLabelItem] .failed #-----| getPattern(): [EnumElementPattern] .failed -#-----| getElement(2): [CallExpr] call to ... -#-----| getFunction(): [UnresolvedDotExpr] ... .combine(_:) -#-----| getBase(): [DeclRefExpr] hasher +#-----| getElement(2): [CallExpr] call to combine(_:) +#-----| getFunction(): [MethodLookupExpr] .combine(_:) +#-----| getBase(): [InOutExpr] &... +#-----| getSubExpr(): [DeclRefExpr] hasher +#-----| getMethodRef(): [DeclRefExpr] combine(_:) #-----| getArgument(0): [Argument] : discriminator #-----| getExpr(): [DeclRefExpr] discriminator +#-----| getExpr().getFullyConverted(): [LoadExpr] (Int) ... #-----| getMember(5): [ConcreteVarDecl] hashValue #-----| Type = Int #-----| getAccessor(0): [Accessor] get diff --git a/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected index 0c4047134f3..81ad590aaab 100644 --- a/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected +++ b/swift/ql/test/library-tests/controlflow/graph/CONSISTENCY/CfgConsistency.expected @@ -8,5 +8,3 @@ multipleSuccessors deadEnd | cfg.swift:33:49:33:60 | call to isZero(x:) | | cfg.swift:144:18:144:34 | ... .&&(_:_:) ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/library-tests/dataflow/taint/libraries/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | 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 deleted file mode 100644 index 4de95c00602..00000000000 --- a/swift/ql/test/library-tests/elements/decl/enumdecl/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,10 +0,0 @@ -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 | ... = ... | diff --git a/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 280e2eeab41..00000000000 --- a/swift/ql/test/library-tests/elements/decl/function/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,3 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-089/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 90f66c1830d..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-1204/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,11 +0,0 @@ -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 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-259/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-311/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 9d02611e7a7..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-312/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,6 +0,0 @@ -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 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 65cf24d02a7..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-321/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,13 +0,0 @@ -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 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index fc0518030f4..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-327/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,9 +0,0 @@ -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 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 069a6ecbc6a..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-328/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,2 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 107533d785d..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-611/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | StmtCondition | -| file://:0:0:0:0 | StmtCondition | -| file://:0:0:0:0 | hasher | -| file://:0:0:0:0 | hasher | diff --git a/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-757/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-760/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | diff --git a/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected b/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected deleted file mode 100644 index 853828d4f77..00000000000 --- a/swift/ql/test/query-tests/Security/CWE-916/CONSISTENCY/CfgConsistency.expected +++ /dev/null @@ -1,5 +0,0 @@ -deadEnd -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | -| file://:0:0:0:0 | ... = ... | From e90153fc471e813cec10661e1a57c57631f37854 Mon Sep 17 00:00:00 2001 From: Robert Marsh Date: Tue, 27 Jun 2023 16:52:45 -0400 Subject: [PATCH 56/93] C++: fix irreducible control flow logic --- .../cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll | 2 +- cpp/ql/test/library-tests/ir/range-analysis/test.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll index 1a5a30d1454..9bc55e4fa80 100644 --- a/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll +++ b/cpp/ql/lib/semmle/code/cpp/rangeanalysis/new/internal/semantic/SemanticSSA.qll @@ -70,7 +70,7 @@ predicate semBackEdge(SemSsaPhiNode phi, SemSsaVariable inp, SemSsaReadPositionP // Conservatively assume that every edge is a back edge if we don't have dominance information. ( phi.getBasicBlock().bbDominates(edge.getOrigBlock()) or - irreducibleSccEdge(phi.getBasicBlock(), edge.getOrigBlock()) or + irreducibleSccEdge(edge.getOrigBlock(), phi.getBasicBlock()) or not edge.getOrigBlock().hasDominanceInformation() ) } 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 1e28d858b78..2271953b7ab 100644 --- a/cpp/ql/test/library-tests/ir/range-analysis/test.cpp +++ b/cpp/ql/test/library-tests/ir/range-analysis/test.cpp @@ -90,7 +90,8 @@ void gotoLoop(bool b1, bool b2) { for (j = 0; j < 10; ++j) { + int x; main_decode_loop: } } -} \ No newline at end of file +} From 0749af79d704b31e662977ce4405661fadcc937e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 28 Jun 2023 00:18:40 +0000 Subject: [PATCH 57/93] Add changed framework coverage reports --- java/documentation/library-coverage/coverage.csv | 6 ++++-- java/documentation/library-coverage/coverage.rst | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 540f282f7c5..02d4c5aa4c8 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -56,8 +56,8 @@ jakarta.ws.rs.core,2,,149,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,,,94,55 java.awt,,,3,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,3 java.beans,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, java.io,49,,45,,,22,,,,,,,,,,,,,,27,,,,,,,,,,,,,,,,,,,43,2 -java.lang,18,,92,,,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,56,36 -java.net,13,3,20,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,20, +java.lang,31,,92,,13,,,,,,,,,,,,8,,,5,,,4,,,1,,,,,,,,,,,,,56,36 +java.net,13,3,21,,,,,,,,,,,,,,,,,,,,,,,,,,13,,,,,,,,,3,21, java.nio,47,,35,,,3,,,,,,,,,,,,,,44,,,,,,,,,,,,,,,,,,,35, java.sql,13,,2,,,,,,,,,,,,,,,,,,,,,,,,,,4,,9,,,,,,,,2, java.util,44,,484,,,,,,,,,,,,,,34,,,,,,,5,2,,1,2,,,,,,,,,,,44,440 @@ -90,11 +90,13 @@ org.apache.commons.codec,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,6, org.apache.commons.collections,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 org.apache.commons.collections4,,,800,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,17,783 org.apache.commons.compress.archivers.tar,,,4,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,4, +org.apache.commons.exec,6,,,,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.httpclient.util,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1, org.apache.commons.io,111,,560,,,2,,,,,,,,,,,,,,94,,,,,,,,,15,,,,,,,,,,546,14 org.apache.commons.jelly,6,,,,,,,,,,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,, org.apache.commons.jexl2,15,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.jexl3,15,,,,,,,,,,,,15,,,,,,,,,,,,,,,,,,,,,,,,,,, +org.apache.commons.lang,,,765,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,594,171 org.apache.commons.lang3,6,,424,,,,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,293,131 org.apache.commons.logging,6,,,,,,,,,,,,,,,,6,,,,,,,,,,,,,,,,,,,,,,, org.apache.commons.net,9,12,,,,,,,,,,,,,,,,,,3,,,,,,,,,6,,,,,,,,,12,, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index dcf4eb2704d..806100ff6d9 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -18,10 +18,10 @@ Java framework & library support `Google Guava `_,``com.google.common.*``,,730,41,7,,,,, JBoss Logging,``org.jboss.logging``,,,324,,,,,, `JSON-java `_,``org.json``,,236,,,,,,, - Java Standard Library,``java.*``,3,682,184,76,,9,,,17 + Java Standard Library,``java.*``,3,683,197,76,,9,,,17 Java extensions,"``javax.*``, ``jakarta.*``",63,611,34,2,4,,1,1,2 Kotlin Standard Library,``kotlin*``,,1847,16,14,,,,,2 `Spring `_,``org.springframework.*``,29,483,115,4,,28,14,,35 - Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,4467,571,89,6,18,18,,200 - Totals,,283,12766,2040,286,16,122,33,1,390 + Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,5232,577,89,6,18,18,,200 + Totals,,283,13532,2059,286,16,122,33,1,390 From 6352399645a9be470b96f0f9de5f4231672e59fe Mon Sep 17 00:00:00 2001 From: Paolo Tranquilli Date: Mon, 26 Jun 2023 17:06:54 +0200 Subject: [PATCH 58/93] Swift: fix all upstream headers for C++20 --- .pre-commit-config.yaml | 4 ++-- swift/third_party/load.bzl | 9 ++++++- .../add-constructor-to-Compilation.patch | 17 +++++++++++++ .../patches/remove-redundant-operators.patch | 24 +++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 swift/third_party/swift-llvm-support/patches/add-constructor-to-Compilation.patch create mode 100644 swift/third_party/swift-llvm-support/patches/remove-redundant-operators.patch diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bb25a64ebfb..2cf6f530354 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,9 +5,9 @@ repos: rev: v3.2.0 hooks: - id: trailing-whitespace - exclude: /test/.*$(? Date: Tue, 27 Jun 2023 09:49:12 +0200 Subject: [PATCH 59/93] Ruby/Python: Use `inline_late` on member predicates --- .../dataflow/new/internal/TypeTracker.qll | 92 ++++++++----------- ruby/ql/lib/codeql/Locations.qll | 19 ++-- .../codeql/ruby/typetracking/TypeTracker.qll | 92 ++++++++----------- 3 files changed, 88 insertions(+), 115 deletions(-) diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll index 25521f5f1a5..fb3d7bf828f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll @@ -235,17 +235,6 @@ private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) { step(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - exists(StepSummary summary | - stepProj(nodeFrom, summary) and - result = t.append(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { smallstepNoCall(nodeFrom, nodeTo, summary) or @@ -257,17 +246,6 @@ private predicate smallstepProj(Node nodeFrom, StepSummary summary) { smallstep(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - smallstepProj(nodeFrom, summary) and - result = t.append(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`. * @@ -501,9 +479,26 @@ class TypeTracker extends TTypeTracker { * Gets the summary that corresponds to having taken a forwards * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. */ - pragma[inline] + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - result = stepInlineLate(this, nodeFrom, nodeTo) + exists(StepSummary summary | + stepProj(nodeFrom, summary) and + result = this.append(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] + private TypeTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + smallstepProj(nodeFrom, summary) and + result = this.append(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -532,7 +527,7 @@ class TypeTracker extends TTypeTracker { */ pragma[inline] TypeTracker smallstep(Node nodeFrom, Node nodeTo) { - result = smallstepInlineLate(this, nodeFrom, nodeTo) + result = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and result = this @@ -552,34 +547,10 @@ private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) { step(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backStepInlineLate( - TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo -) { - exists(StepSummary summary | - backStepProj(nodeTo, summary) and - result = t.prepend(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) { smallstep(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - backSmallstepProj(nodeTo, summary) and - result = t.prepend(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * A summary of the steps needed to back-track a use of a value to a given dataflow node. * @@ -661,9 +632,26 @@ class TypeBackTracker extends TTypeBackTracker { * Gets the summary that corresponds to having taken a backwards * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. */ - pragma[inline] + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - this = backStepInlineLate(result, nodeFrom, nodeTo) + exists(StepSummary summary | + backStepProj(nodeTo, summary) and + this = result.prepend(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] + private TypeBackTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + backSmallstepProj(nodeTo, summary) and + this = result.prepend(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -692,7 +680,7 @@ class TypeBackTracker extends TTypeBackTracker { */ pragma[inline] TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) { - this = backSmallstepInlineLate(result, nodeFrom, nodeTo) + this = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and this = result diff --git a/ruby/ql/lib/codeql/Locations.qll b/ruby/ql/lib/codeql/Locations.qll index 3a16bdec40d..87198146d88 100644 --- a/ruby/ql/lib/codeql/Locations.qll +++ b/ruby/ql/lib/codeql/Locations.qll @@ -2,15 +2,6 @@ import files.FileSystem -bindingset[loc] -pragma[inline_late] -private string locationToString(Location loc) { - exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | - loc.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and - result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn - ) -} - /** * A location as given by a file, a start line, a start column, * an end line, and an end column. @@ -37,8 +28,14 @@ class Location extends @location_default { int getNumLines() { result = this.getEndLine() - this.getStartLine() + 1 } /** Gets a textual representation of this element. */ - pragma[inline] - string toString() { result = locationToString(this) } + bindingset[this] + pragma[inline_late] + string toString() { + exists(string filepath, int startline, int startcolumn, int endline, int endcolumn | + this.hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) and + result = filepath + "@" + startline + ":" + startcolumn + ":" + endline + ":" + endcolumn + ) + } /** * Holds if this element is at the specified location. diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll index 25521f5f1a5..fb3d7bf828f 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -235,17 +235,6 @@ private predicate stepProj(TypeTrackingNode nodeFrom, StepSummary summary) { step(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker stepInlineLate(TypeTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - exists(StepSummary summary | - stepProj(nodeFrom, summary) and - result = t.append(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate smallstep(Node nodeFrom, TypeTrackingNode nodeTo, StepSummary summary) { smallstepNoCall(nodeFrom, nodeTo, summary) or @@ -257,17 +246,6 @@ private predicate smallstepProj(Node nodeFrom, StepSummary summary) { smallstep(nodeFrom, _, summary) } -bindingset[nodeFrom, t] -pragma[inline_late] -pragma[noopt] -private TypeTracker smallstepInlineLate(TypeTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - smallstepProj(nodeFrom, summary) and - result = t.append(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * Holds if `nodeFrom` is being written to the `content` of the object in `nodeTo`. * @@ -501,9 +479,26 @@ class TypeTracker extends TTypeTracker { * Gets the summary that corresponds to having taken a forwards * heap and/or inter-procedural step from `nodeFrom` to `nodeTo`. */ - pragma[inline] + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] TypeTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - result = stepInlineLate(this, nodeFrom, nodeTo) + exists(StepSummary summary | + stepProj(nodeFrom, summary) and + result = this.append(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeFrom, this] + pragma[inline_late] + pragma[noopt] + private TypeTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + smallstepProj(nodeFrom, summary) and + result = this.append(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -532,7 +527,7 @@ class TypeTracker extends TTypeTracker { */ pragma[inline] TypeTracker smallstep(Node nodeFrom, Node nodeTo) { - result = smallstepInlineLate(this, nodeFrom, nodeTo) + result = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and result = this @@ -552,34 +547,10 @@ private predicate backStepProj(TypeTrackingNode nodeTo, StepSummary summary) { step(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backStepInlineLate( - TypeBackTracker t, TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo -) { - exists(StepSummary summary | - backStepProj(nodeTo, summary) and - result = t.prepend(summary) and - step(nodeFrom, nodeTo, summary) - ) -} - private predicate backSmallstepProj(TypeTrackingNode nodeTo, StepSummary summary) { smallstep(_, nodeTo, summary) } -bindingset[nodeTo, t] -pragma[inline_late] -pragma[noopt] -private TypeBackTracker backSmallstepInlineLate(TypeBackTracker t, Node nodeFrom, Node nodeTo) { - exists(StepSummary summary | - backSmallstepProj(nodeTo, summary) and - result = t.prepend(summary) and - smallstep(nodeFrom, nodeTo, summary) - ) -} - /** * A summary of the steps needed to back-track a use of a value to a given dataflow node. * @@ -661,9 +632,26 @@ class TypeBackTracker extends TTypeBackTracker { * Gets the summary that corresponds to having taken a backwards * heap and/or inter-procedural step from `nodeTo` to `nodeFrom`. */ - pragma[inline] + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] TypeBackTracker step(TypeTrackingNode nodeFrom, TypeTrackingNode nodeTo) { - this = backStepInlineLate(result, nodeFrom, nodeTo) + exists(StepSummary summary | + backStepProj(nodeTo, summary) and + this = result.prepend(summary) and + step(nodeFrom, nodeTo, summary) + ) + } + + bindingset[nodeTo, result] + pragma[inline_late] + pragma[noopt] + private TypeBackTracker smallstepNoSimpleLocalFlowStep(Node nodeFrom, Node nodeTo) { + exists(StepSummary summary | + backSmallstepProj(nodeTo, summary) and + this = result.prepend(summary) and + smallstep(nodeFrom, nodeTo, summary) + ) } /** @@ -692,7 +680,7 @@ class TypeBackTracker extends TTypeBackTracker { */ pragma[inline] TypeBackTracker smallstep(Node nodeFrom, Node nodeTo) { - this = backSmallstepInlineLate(result, nodeFrom, nodeTo) + this = this.smallstepNoSimpleLocalFlowStep(nodeFrom, nodeTo) or simpleLocalFlowStep(nodeFrom, nodeTo) and this = result From e4d2c51ff86334dd8206dc730b52c41eb754db03 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Jun 2023 09:40:31 +0200 Subject: [PATCH 60/93] C#: Add tests for names of nested classes --- .../Telemetry/LibraryUsage/ExternalLibraryUsage.cs | 6 ++++++ .../Telemetry/LibraryUsage/ExternalLibraryUsage.expected | 2 +- .../Telemetry/LibraryUsage/SupportedExternalTaint.expected | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs index aaef1776cd3..1ec45beac39 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.cs @@ -25,4 +25,10 @@ public class LibraryUsage { var guid1 = Guid.Parse("{12345678-1234-1234-1234-123456789012}"); // Has no flow summary } + + public void M4() + { + var d = new Dictionary(); // Uninteresting parameterless constructor + var e = d.Keys.GetEnumerator().MoveNext(); // Methods on nested classes + } } diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected index 7b5c4c57661..54f3920572e 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/ExternalLibraryUsage.expected @@ -1,2 +1,2 @@ | System | 5 | -| System.Collections.Generic | 2 | +| System.Collections.Generic | 5 | diff --git a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected index b386e4acb38..c9f63591888 100644 --- a/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected +++ b/csharp/ql/test/query-tests/Telemetry/LibraryUsage/SupportedExternalTaint.expected @@ -1 +1,3 @@ | System.Collections.Generic#List<>.Add(T) | 2 | +| System.Collections.Generic#Dictionary<,>+KeyCollection.GetEnumerator() | 1 | +| System.Collections.Generic#Dictionary<,>.get_Keys() | 1 | From 51af03d2bca2f0c7ae756a1da0c7e04c5f1f9b0d Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 28 Jun 2023 09:52:25 +0200 Subject: [PATCH 61/93] Java: Add tests for names of nested classes --- .../SupportedExternalApis/SupportedExternalApis.expected | 4 ++++ .../SupportedExternalApis/SupportedExternalApis.java | 1 + 2 files changed, 5 insertions(+) diff --git a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected index 3f5b3663281..199e9413ecf 100644 --- a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected +++ b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.expected @@ -7,5 +7,9 @@ | java.net.URL#openStream() | 1 | | java.net.URLConnection#getInputStream() | 1 | | java.time.Duration#ofMillis(long) | 1 | +| java.util.Iterator#next() | 1 | +| java.util.Map#entrySet() | 1 | | java.util.Map#put(Object,Object) | 1 | +| java.util.Map$Entry#getKey() | 1 | +| java.util.Set#iterator() | 1 | | org.apache.commons.io.FileUtils#deleteDirectory(File) | 1 | diff --git a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java index 6445e97b473..ac22f5065a4 100644 --- a/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java +++ b/java/ql/test/query-tests/Telemetry/SupportedExternalApis/SupportedExternalApis.java @@ -15,6 +15,7 @@ class SupportedExternalApis { Map map = new HashMap<>(); // uninteresting (parameterless constructor) map.put("foo", new Object()); // supported summary + map.entrySet().iterator().next().getKey(); // nested class (Map.Entry), supported summaries (entrySet, iterator, next, getKey) Duration d = java.time.Duration.ofMillis(1000); // supported neutral From 78f2fe8d5e3763776c737a1b682afeff374f0393 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 28 Jun 2023 10:13:03 +0100 Subject: [PATCH 62/93] C++: Fix join in 'argumentOf'. Before: ``` [2023-06-28 09:29:51] Evaluated non-recursive predicate DataFlowImplCommon#59e7a193::Cached::argumentNode#3#fff@8606bd35 in 1945ms (size: 1366058). Evaluated relational algebra for predicate DataFlowImplCommon#59e7a193::Cached::argumentNode#3#fff@8606bd35 with tuple counts: 764401 ~0% {3} r1 = JOIN DataFlowPrivate#fbdd7bd7::DirectPosition#ff_10#join_rhs WITH Instruction#577b6a83::CallInstruction::getArgumentOperand#fff_102#join_rhs ON FIRST 1 OUTPUT Rhs.2, Lhs.1, Rhs.1 764401 ~0% {3} r2 = JOIN r1 WITH DataFlowPrivate#fbdd7bd7::PrimaryArgumentNode#fff_20#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.2, Lhs.1 65 ~0% {3} r3 = SCAN DataFlowPrivate#fbdd7bd7::IndirectionPosition#fff OUTPUT In.2, In.0, In.1 180518864 ~0% {3} r4 = JOIN r3 WITH project#DataFlowPrivate#fbdd7bd7::IndirectOperands::IndirectOperand::hasOperandAndIndirectionIndex#2#dispred#fff#3_10#join_rhs ON FIRST 1 OUTPUT Rhs.1, Lhs.2, Lhs.1 601657 ~1% {2} r5 = JOIN r4 WITH project#DataFlowUtil#47741e1f::SideEffectOperandNode#fff#2 ON FIRST 2 OUTPUT Lhs.0, Lhs.2 601657 ~0% {3} r6 = JOIN r5 WITH project#DataFlowUtil#47741e1f::SideEffectOperandNode#fff#3 ON FIRST 1 OUTPUT Lhs.0, Rhs.1, Lhs.1 1366058 ~0% {3} r7 = r2 UNION r6 return r7 ``` After: ``` Tuple counts for DataFlowImplCommon#59e7a193::Cached::argumentNode#3#fff/3@d2b091vc after 1.1s: 764381 ~2% {3} r1 = JOIN DataFlowPrivate#fbdd7bd7::DirectPosition#ff_10#join_rhs WITH Instruction#577b6a83::CallInstruction::getArgumentOperand#fff_102#join_rhs ON FIRST 1 OUTPUT Rhs.2, Lhs.1 'pos', Rhs.1 'call' 764381 ~0% {3} r2 = JOIN r1 WITH DataFlowPrivate#fbdd7bd7::PrimaryArgumentNode#fff_20#join_rhs ON FIRST 1 OUTPUT Rhs.1 'n', Lhs.2 'call', Lhs.1 'pos' 65 ~3% {3} r3 = SCAN num#DataFlowPrivate#fbdd7bd7::TIndirectionPosition#fff OUTPUT In.0, In.2 'pos', In.1 1798930 ~1% {3} r4 = JOIN r3 WITH project#DataFlowUtil#47741e1f::SideEffectOperandNode#fff#2_10#join_rhs ON FIRST 1 OUTPUT Rhs.1 'n', Lhs.2, Lhs.1 'pos' 601641 ~1% {2} r5 = JOIN r4 WITH project#DataFlowPrivate#fbdd7bd7::IndirectOperands::IndirectOperand::hasOperandAndIndirectionIndex#2#dispred#fff#3 ON FIRST 2 OUTPUT Lhs.0 'n', Lhs.2 'pos' 601641 ~0% {3} r6 = JOIN r5 WITH project#DataFlowUtil#47741e1f::SideEffectOperandNode#fff#3 ON FIRST 1 OUTPUT Lhs.0 'n', Rhs.1 'call', Lhs.1 'pos' 1366022 ~1% {3} r7 = r2 UNION r6 return r7 ``` --- .../code/cpp/ir/dataflow/internal/DataFlowPrivate.qll | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 31915a888ac..59dfe5ed9d4 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -321,9 +321,11 @@ private class PrimaryArgumentNode extends ArgumentNode, OperandNode { private class SideEffectArgumentNode extends ArgumentNode, SideEffectOperandNode { override predicate argumentOf(DataFlowCall dfCall, ArgumentPosition pos) { - this.getCallInstruction() = dfCall and - pos.(IndirectionPosition).getArgumentIndex() = this.getArgumentIndex() and - super.hasAddressOperandAndIndirectionIndex(_, pos.(IndirectionPosition).getIndirectionIndex()) + exists(int indirectionIndex | + pos = TIndirectionPosition(argumentIndex, pragma[only_bind_into](indirectionIndex)) and + this.getCallInstruction() = dfCall and + super.hasAddressOperandAndIndirectionIndex(_, pragma[only_bind_into](indirectionIndex)) + ) } } From dbdd6544652b83aab4168c457292c56490f25bd5 Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Wed, 28 Jun 2023 12:11:17 +0200 Subject: [PATCH 63/93] Swift: bump Swift build, NFC --- swift/third_party/load.bzl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/swift/third_party/load.bzl b/swift/third_party/load.bzl index 4bceff6ec7a..9cefc77948d 100644 --- a/swift/third_party/load.bzl +++ b/swift/third_party/load.bzl @@ -4,11 +4,11 @@ load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") # TODO: remove `remove-result-of.patch` once we update to a Swift version containing # https://github.com/apple/swift/commit/2ed2cea2 # (probably when updating to 5.9) -_swift_prebuilt_version = "swift-5.8.1-RELEASE.210" +_swift_prebuilt_version = "swift-5.8.1-RELEASE.212" _swift_sha_map = { - "Linux-X64": "d8c715044c3989683b3f986a377647697245ed6bbdc2add13890433c9dc732a4", - "macOS-ARM64": "2ca169f299bce61034bd011f081a55be07a19f20fd7ea855b241f43fe0fb76d2", - "macOS-X64": "80e2e8cefbd78d71e54923c1da36cac3511b4c69c4841cf35bef383bc6750d18", + "Linux-X64": "3e902cc9dbf02129f6bcac84902524a235df0e36d30f3ac54e642b64d3f95a2b", + "macOS-ARM64": "4d93f326bd8a41c89bcf593676407fab2dd84b665f6bfb7ab667a9673084bcda", + "macOS-X64": "988cd193a0590abd282d8d8f3ec2489583d3d2b34162a4e91208fb91e5fb5981", } _swift_arch_map = { From ae098822c32400601ae7cce7ae0b4d2183d0671b Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 28 Jun 2023 11:21:24 +0100 Subject: [PATCH 64/93] C++: Add 'cpp/constant-array-overflow' FP. --- .../ConstantSizeArrayOffByOne.expected | 33 +++++++++++++ .../CWE/CWE-193/constant-size/test.cpp | 49 +++++++++++++++++++ 2 files changed, 82 insertions(+) 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 ff75c77e702..3871d5b9e40 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 @@ -39,6 +39,21 @@ edges | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | | test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array | | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | +| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array | +| test.cpp:198:14:198:20 | buffer1 | test.cpp:175:30:175:30 | p | +| test.cpp:198:14:198:20 | buffer1 | test.cpp:198:14:198:20 | buffer1 | +| test.cpp:201:14:201:20 | buffer2 | test.cpp:175:30:175:30 | p | +| test.cpp:201:14:201:20 | buffer2 | test.cpp:201:14:201:20 | buffer2 | +| test.cpp:204:14:204:20 | buffer3 | test.cpp:175:30:175:30 | p | +| test.cpp:204:14:204:20 | buffer3 | test.cpp:204:14:204:20 | buffer3 | +| test.cpp:207:35:207:35 | p | test.cpp:208:14:208:14 | p | +| test.cpp:208:14:208:14 | p | test.cpp:175:30:175:30 | p | +| test.cpp:213:19:213:25 | buffer1 | test.cpp:207:35:207:35 | p | +| test.cpp:213:19:213:25 | buffer1 | test.cpp:213:19:213:25 | buffer1 | +| test.cpp:216:19:216:25 | buffer2 | test.cpp:207:35:207:35 | p | +| test.cpp:216:19:216:25 | buffer2 | test.cpp:216:19:216:25 | buffer2 | +| test.cpp:219:19:219:25 | buffer3 | test.cpp:207:35:207:35 | p | +| test.cpp:219:19:219:25 | buffer3 | test.cpp:219:19:219:25 | buffer3 | nodes | test.cpp:34:5:34:24 | access to array | semmle.label | access to array | | test.cpp:34:10:34:12 | buf | semmle.label | buf | @@ -97,6 +112,22 @@ nodes | test.cpp:159:25:159:29 | array | semmle.label | array | | test.cpp:161:5:161:10 | access to array | semmle.label | access to array | | test.cpp:162:5:162:10 | access to array | semmle.label | access to array | +| test.cpp:175:30:175:30 | p | semmle.label | p | +| test.cpp:191:27:191:30 | access to array | semmle.label | access to array | +| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 | +| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 | +| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 | +| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 | +| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | +| test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | +| test.cpp:207:35:207:35 | p | semmle.label | p | +| test.cpp:208:14:208:14 | p | semmle.label | p | +| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 | +| test.cpp:213:19:213:25 | buffer1 | semmle.label | buffer1 | +| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 | +| test.cpp:216:19:216:25 | buffer2 | semmle.label | buffer2 | +| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 | +| test.cpp:219:19:219:25 | buffer3 | semmle.label | buffer3 | 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 | @@ -113,3 +144,5 @@ subpaths | 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 | | test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write | | test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write | +| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:201:14:201:20 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:200:19:200:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read | +| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:216:19:216:25 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:215:19:215:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | 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 902bf5a2cd9..5a618d1c0b2 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 @@ -169,3 +169,52 @@ void pointer_size_larger_than_array_element_size_and_does_not_divide_it() { ptr[0] = vec3{}; // GOOD: writes ints 0, 1, 2 ptr[1] = vec3{}; // BAD: writes ints 3, 4, 5 [NOT DETECTED] } + +void use(...); + +void call_use(unsigned char* p, int n) { + if(n == 0) { + return; + } + if(n == 1) { + unsigned char x = p[0]; + use(x); + } + if(n == 2) { + unsigned char x = p[0]; + unsigned char y = p[1]; + use(x, y); + } + if(n == 3) { + unsigned char x = p[0]; + unsigned char y = p[1]; + unsigned char z = p[2]; // GOOD [FALSE POSITIVE]: `call_use(buffer2, 2)` won't reach this point. + use(x, y, z); + } +} + +void test_call_use() { + unsigned char buffer1[1]; + call_use(buffer1,1); + + unsigned char buffer2[2]; + call_use(buffer2,2); + + unsigned char buffer3[3]; + call_use(buffer3,3); +} + +void call_call_use(unsigned char* p, int n) { + call_use(p, n); +} + +void test_call_use2() { + unsigned char buffer1[1]; + call_call_use(buffer1,1); + + unsigned char buffer2[2]; + call_call_use(buffer2,2); + + unsigned char buffer3[3]; + call_call_use(buffer3,3); +} From 174ab25867cec523db8a2b642fffd45922519ffa Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 10:58:10 +0200 Subject: [PATCH 65/93] Ruby: address some review comments --- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 9 ++++----- .../lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll | 5 ++--- ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll | 2 +- ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll | 1 - 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 2dd070ee0df..b643a022014 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -1,6 +1,6 @@ /** * Provides an implementation of _API graphs_, which allow efficient modelling of how a given - * value is used the code base or how values produced by the code base are consumed by a library. + * value is used by the code base or how values produced by the code base are consumed by a library. * * See `API::Node` for more details. */ @@ -53,7 +53,7 @@ module API { * * The members predicates on this class generally take inheritance and data flow into account. * - * The following example demonstrate a case where data flow was used to find the sink `x`: + * The following example demonstrates a case where data flow was used to find the sink `x`: * ```ruby * def doSomething f * f.bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() @@ -280,7 +280,6 @@ module API { /** * Gets an access to the constant `m` with this value as the base of the access. * - * For example, the constant `A::B` would be found by `API::getATopLevelMember("A").getMember("B")` * For example: * ```ruby * A::B # API::getATopLevelMember("A").getMember("B") @@ -761,7 +760,7 @@ module API { /** * A node corresponding to an argument, right-hand side of a store, or return value from a callable. * - * Such a node may serve as the starting-point of backtracking, and has epsilon edges going + * Such a node may serve as the starting-point of backtracking, and has epsilon edges going to * the backward nodes corresponding to `getALocalSource`. */ private class SinkNode extends Node, Impl::MkSinkNode { @@ -905,7 +904,7 @@ module API { } /** - * Holds if the epsilon `pred -> succ` be generated, to associate `mod` with its references in the codebase. + * Holds if the epsilon `pred -> succ` should be generated, to associate `mod` with its references in the codebase. */ bindingset[mod] pragma[inline_late] diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index d6fcc1ddf13..d2fb1354798 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -419,7 +419,7 @@ private module Cached { } /** - * Gets a module for which this constant is the reference to an ancestor module. + * Gets a module for which `constRef` is the reference to an ancestor module. * * For example, `M` is the ancestry target of `C` in the following examples: * ```rb @@ -437,11 +437,10 @@ private module Cached { private ModuleNode getAncestryTarget(ConstRef constRef) { result.getAnAncestorExpr() = constRef } /** - * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant. + * Gets a scope in which a constant lookup may access the contents of the module referenced by `constRef`. */ cached TConstLookupScope getATargetScope(ConstRef constRef) { - forceCachingInSameStage() and result = MkAncestorLookup(getAncestryTarget(constRef).getAnImmediateDescendent*()) or constRef.asConstantAccess() = any(ConstantAccess ac).getScopeExpr() and diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index f9c15a4c211..4a5d342eeba 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -128,7 +128,7 @@ class ActiveRecordModelClass extends ClassDeclaration { ) } - /** Gets the class as a `DataFlow::ClasNode`. */ + /** Gets the class as a `DataFlow::ClassNode`. */ DataFlow::ClassNode getClassNode() { result = cls } } diff --git a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll index e27384b1c7b..07b10cb1303 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll @@ -84,7 +84,6 @@ module ApiGraphShared { pragma[noopt] cached predicate epsilonEdge(ApiNode pred, ApiNode succ) { - // forward exists( StepSummary summary, TypeTrackingNode predNode, TypeTracker predState, TypeTrackingNode succNode, TypeTracker succState From 67032b5d733d8f863772bbbdba98aae493237af3 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:01:06 +0200 Subject: [PATCH 66/93] Ruby: add test for self.class call --- .../dataflow/api-graphs/self-dot-class.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb new file mode 100644 index 00000000000..178cacbe2c0 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb @@ -0,0 +1,10 @@ +module SelfDotClass + module Mixin + def foo + self.class.bar # $ call=Member[Foo].Method[bar] + end + end + class Subclass < Foo + include Mixin + end +end From f171c21002094891a57ba4b44a25bac340928e50 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:44:51 +0200 Subject: [PATCH 67/93] Ruby: remove forwarder for getADescendentModule --- .../codeql/ruby/dataflow/internal/DataFlowPublic.qll | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index d2fb1354798..7772e5235d7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -1518,14 +1518,9 @@ class ConstRef extends LocalSourceNode { * end * ``` */ - pragma[inline] - ModuleNode getADescendentModule() { result = getADescendentModuleInline(this) } -} - -bindingset[ref] -pragma[inline_late] -private ModuleNode getADescendentModuleInline(ConstRef ref) { - MkAncestorLookup(result) = getATargetScope(ref) + bindingset[this] + pragma[inline_late] + ModuleNode getADescendentModule() { MkAncestorLookup(result) = getATargetScope(this) } } /** From 6feda75dd66257f26eb22945afe54a7cca8db39a Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:44:59 +0200 Subject: [PATCH 68/93] Ruby: preserve comment in SQLite3 --- ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll | 1 + 1 file changed, 1 insertion(+) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index 208bff8d8c9..dfb93a0e6e5 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll @@ -22,6 +22,7 @@ module Sqlite3 { private API::Node dbInstance() { result = databaseConst().getInstance() or + // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } result = databaseConst().getMethod("new").getBlock().getParameter(0) } From dd868437ce49792f84f311823b3cd546cb38569e Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:44:29 +0200 Subject: [PATCH 69/93] Ruby: add asCallable() --- ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index b643a022014..0d6e669b1de 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -200,6 +200,25 @@ module API { pragma[inline_late] DataFlow::Node asSink() { result = asSinkInline(this) } + /** + * Gets a callable that can reach this sink. + * + * For example: + * ```ruby + * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().asCallable() + * end + * + * class Baz + * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m").asCallable() + * end + * end + * Foo.bar(Baz.new) + * ``` + */ + bindingset[this] + pragma[inline_late] + DataFlow::CallableNode asCallable() { Impl::asCallable(this.getAnEpsilonSuccessor(), result) } + /** * Get a data-flow node that transitively flows to this value, provided that this value corresponds * to a sink. @@ -1196,6 +1215,11 @@ module API { ) } + cached + predicate asCallable(Node apiNode, DataFlow::CallableNode callable) { + apiNode = getBackwardStartNode(callable) + } + cached predicate contentEdge(Node pred, DataFlow::Content content, Node succ) { exists( From 423da55fb9e79b8ee71ff41db0b9638377e75ec2 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:47:15 +0200 Subject: [PATCH 70/93] Ruby: use asCallable() in Twirp model --- ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll index a315ae100ce..eec7ce30e24 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll @@ -21,16 +21,17 @@ module Twirp { this = API::getTopLevelMember("Twirp").getMember("Service").getAnInstantiation() } - private DataFlow::ClassNode getAHandlerClass() { - result.getAnImmediateReference().getAMethodCall("new").flowsTo(this.getArgument(0)) - } - /** * Gets a handler's method. */ - Ast::Method getAHandlerMethod() { - result = this.getAHandlerClass().getAnAncestor().getAnOwnInstanceMethod().asCallableAstNode() + DataFlow::MethodNode getAHandlerMethodNode() { + result = this.getArgument(0).backtrack().getMethod(_).asCallable() } + + /** + * Gets a handler's method as an AST node. + */ + Ast::Method getAHandlerMethod() { result = this.getAHandlerMethodNode().asCallableAstNode() } } /** @@ -52,7 +53,7 @@ module Twirp { DataFlow::ParameterNode { UnmarshaledParameter() { - exists(ServiceInstantiation i | i.getAHandlerMethod().getParameter(0) = this.asParameter()) + this = any(ServiceInstantiation i).getAHandlerMethodNode().getParameter(0) } override string getSourceType() { result = "Twirp Unmarhaled Parameter" } From 129e6349f7498589b10e5e4bedac97f8cddcdb43 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 11:47:46 +0200 Subject: [PATCH 71/93] Ruby: expand Twirp test --- .../library-tests/frameworks/Twirp/Twirp.expected | 2 ++ .../frameworks/Twirp/hello_world_server.rb | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected index 66da43ab78b..4f1b0c30920 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected @@ -1,6 +1,8 @@ sourceTest | hello_world_server.rb:8:13:8:15 | req | +| hello_world_server.rb:32:18:32:20 | req | ssrfSinkTest | hello_world_client.rb:6:47:6:75 | "http://localhost:8080/twirp" | serviceInstantiationTest | hello_world_server.rb:24:11:24:61 | call to new | +| hello_world_server.rb:38:1:38:57 | call to new | diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb index 1aa0b9aa8de..7cd117a5843 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb +++ b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb @@ -5,7 +5,7 @@ require_relative 'hello_world/service_twirp.rb' class HelloWorldHandler # test: request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -13,7 +13,7 @@ end class FakeHelloWorldHandler # test: !request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -21,9 +21,18 @@ end handler = HelloWorldHandler.new() # test: serviceInstantiation -service = Example::HelloWorld::HelloWorldService.new(handler) +service = Example::HelloWorld::HelloWorldService.new(handler) path_prefix = "/twirp/" + service.full_name server = WEBrick::HTTPServer.new(Port: 8080) server.mount path_prefix, Rack::Handler::WEBrick, service server.start + +class StaticHandler + def self.hello(req, env) + puts ">> Hello #{req.name}" + {message: "Hello #{req.name}"} + end +end + +Example::HelloWorld::HelloWorldService.new(StaticHandler) From 7af3d226c9a884fc1a93383a50e2217d42e2e80d Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 12:55:05 +0200 Subject: [PATCH 72/93] Ruby: simplify Twirp model --- ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll | 18 ++++++++++++++---- .../library-tests/frameworks/Twirp/Twirp.ql | 2 +- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll index eec7ce30e24..7b8648bd2b1 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll @@ -16,7 +16,7 @@ module Twirp { /** * A Twirp service instantiation */ - class ServiceInstantiation extends DataFlow::CallNode { + deprecated class ServiceInstantiation extends DataFlow::CallNode { ServiceInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Service").getAnInstantiation() } @@ -37,7 +37,7 @@ module Twirp { /** * A Twirp client */ - class ClientInstantiation extends DataFlow::CallNode { + deprecated class ClientInstantiation extends DataFlow::CallNode { ClientInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Client").getAnInstantiation() } @@ -45,7 +45,10 @@ module Twirp { /** The URL of a Twirp service, considered as a sink. */ class ServiceUrlAsSsrfSink extends ServerSideRequestForgery::Sink { - ServiceUrlAsSsrfSink() { exists(ClientInstantiation c | c.getArgument(0) = this) } + ServiceUrlAsSsrfSink() { + this = + API::getTopLevelMember("Twirp").getMember("Client").getMethod("new").getArgument(0).asSink() + } } /** A parameter that will receive parts of the url when handling an incoming request. */ @@ -53,7 +56,14 @@ module Twirp { DataFlow::ParameterNode { UnmarshaledParameter() { - this = any(ServiceInstantiation i).getAHandlerMethodNode().getParameter(0) + this = + API::getTopLevelMember("Twirp") + .getMember("Service") + .getMethod("new") + .getArgument(0) + .getMethod(_) + .getParameter(0) + .asSource() } override string getSourceType() { result = "Twirp Unmarhaled Parameter" } diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql index 4c0494f9100..fee49cbb48c 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql @@ -5,4 +5,4 @@ query predicate sourceTest(Twirp::UnmarshaledParameter source) { any() } query predicate ssrfSinkTest(Twirp::ServiceUrlAsSsrfSink sink) { any() } -query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } +deprecated query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } From 2f1223426a40427368e9ac08c914575e208b6812 Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 13:36:47 +0200 Subject: [PATCH 73/93] Ruby: add change note --- ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md diff --git a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md new file mode 100644 index 00000000000..37740acd9ee --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md @@ -0,0 +1,7 @@ +--- +category: library +--- +* The API graph library (`codeql.ruby.ApiGraphs`) has been significantly improved, with better support for inheritance, + and data-flow nodes can now be converted to API nodes by calling `.track()` or `.backtrack()` on the node. + API graphs allow for efficient modelling of how a given value is used by the code base, or how values produced by the code base + are consumed by a library. See the documentation for `API::Node` for details and examples. From 39789d4050a5b0f4f8be0c84935d0a2cc66dcc7c Mon Sep 17 00:00:00 2001 From: Asger F Date: Wed, 28 Jun 2023 13:42:05 +0200 Subject: [PATCH 74/93] Ruby: use a valid change note category --- ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md index 37740acd9ee..5aa79d5e2f3 100644 --- a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md +++ b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md @@ -1,5 +1,5 @@ --- -category: library +category: majorAnalysis --- * The API graph library (`codeql.ruby.ApiGraphs`) has been significantly improved, with better support for inheritance, and data-flow nodes can now be converted to API nodes by calling `.track()` or `.backtrack()` on the node. From 14609a97954b4151c41132777e69c1f52a766485 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Tue, 27 Jun 2023 19:48:42 +0200 Subject: [PATCH 75/93] C++: Add barrier to `InvalidPointerToDerefConfig` in `cpp/invalid-pointer-deref` --- .../CWE/CWE-193/InvalidPointerDeref.ql | 66 ++- .../InvalidPointerDeref.expected | 539 ++++++++++++++---- .../CWE/CWE-193/pointer-deref/test.cpp | 127 ++++- 3 files changed, 599 insertions(+), 133 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 6659b820659..c1f7e735636 100644 --- a/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql +++ b/cpp/ql/src/experimental/Security/CWE/CWE-193/InvalidPointerDeref.ql @@ -312,11 +312,13 @@ Instruction getASuccessor(Instruction instr) { */ pragma[inline] predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string operation, int delta) { - exists(AddressOperand addr, Instruction s | + exists(AddressOperand addr, Instruction s, IRBlock b | s = sink.asInstruction() and - bounded1(addr.getDef(), s, delta) and + boundedImpl(addr.getDef(), s, delta) and delta >= 0 and - i.getAnOperand() = addr + i.getAnOperand() = addr and + b = i.getBlock() and + not b = InvalidPointerToDerefBarrier::getABarrierBlock(delta) | i instanceof StoreInstruction and operation = "write" @@ -326,6 +328,60 @@ predicate isInvalidPointerDerefSink(DataFlow::Node sink, Instruction i, string o ) } +module InvalidPointerToDerefBarrier { + private module BarrierConfig implements DataFlow::ConfigSig { + predicate isSource(DataFlow::Node source) { + // The sources is the same as in the sources for `InvalidPointerToDerefConfig`. + invalidPointerToDerefSource(_, source, _) + } + + additional predicate isSink( + DataFlow::Node left, DataFlow::Node right, IRGuardCondition g, int state, boolean testIsTrue + ) { + // The sink is any "large" side of a relational comparison. + g.comparesLt(left.asOperand(), right.asOperand(), state, true, testIsTrue) + } + + predicate isSink(DataFlow::Node sink) { isSink(_, sink, _, _, _) } + } + + private import DataFlow::Global + + private int getInvalidPointerToDerefSourceDelta(DataFlow::Node node) { + exists(DataFlow::Node source | + flow(source, node) and + invalidPointerToDerefSource(_, source, result) + ) + } + + private predicate operandGuardChecks( + IRGuardCondition g, Operand left, Operand right, int state, boolean edge + ) { + exists(DataFlow::Node nLeft, DataFlow::Node nRight, int state0 | + nRight.asOperand() = right and + nLeft.asOperand() = left and + BarrierConfig::isSink(nLeft, nRight, g, state0, edge) and + state = getInvalidPointerToDerefSourceDelta(nRight) and + state0 <= state + ) + } + + Instruction getABarrierInstruction(int state) { + exists(IRGuardCondition g, ValueNumber value, Operand use, boolean edge | + use = value.getAUse() and + operandGuardChecks(pragma[only_bind_into](g), pragma[only_bind_into](use), _, state, + pragma[only_bind_into](edge)) and + result = value.getAnInstruction() and + g.controls(result.getBlock(), edge) + ) + } + + DataFlow::Node getABarrierNode() { result.asOperand() = getABarrierInstruction(_).getAUse() } + + pragma[nomagic] + IRBlock getABarrierBlock(int state) { result.getAnInstruction() = getABarrierInstruction(state) } +} + /** * A configuration to track flow from a pointer-arithmetic operation found * by `AllocToInvalidPointerConfig` to a dereference of the pointer. @@ -338,6 +394,8 @@ module InvalidPointerToDerefConfig implements DataFlow::ConfigSig { predicate isBarrier(DataFlow::Node node) { node = any(DataFlow::SsaPhiNode phi | not phi.isPhiRead()).getAnInput(true) + or + node = InvalidPointerToDerefBarrier::getABarrierNode() } } @@ -382,7 +440,7 @@ newtype TMergedPathNode = // pointer, but we want to raise an alert at the dereference. TPathNodeSink(Instruction i) { exists(DataFlow::Node n | - InvalidPointerToDerefFlow::flowTo(n) and + InvalidPointerToDerefFlow::flowTo(pragma[only_bind_into](n)) and isInvalidPointerDerefSink(n, i, _, _) and i = getASuccessor(n.asInstruction()) ) 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 c0fd4fb29dd..b1ecd38c3c3 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 @@ -720,14 +720,6 @@ edges | 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: * ... | | test.cpp:377:14:377:27 | new[] | test.cpp:378:15:378:16 | xs | | test.cpp:378:15:378:16 | xs | test.cpp:378:15:378:23 | ... + ... | | test.cpp:378:15:378:16 | xs | test.cpp:378:15:378:23 | ... + ... | @@ -752,53 +744,284 @@ edges | test.cpp:381:5:381:9 | ... ++ | test.cpp:384:14:384:16 | end | | test.cpp:384:14:384:16 | end | test.cpp:384:13:384:16 | Load: * ... | | test.cpp:388:14:388:27 | new[] | test.cpp:389:16:389:17 | xs | -| test.cpp:388:14:388:27 | new[] | test.cpp:392:5:392:6 | xs | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:389:16:389:17 | xs | test.cpp:393:9:393:10 | xs | -| test.cpp:389:16:389:17 | xs | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:392:5:392:8 | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:393:9:393:10 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:6 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:6 | xs | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:392:5:392:8 | ... ++ | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:6 | xs | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:393:9:393:10 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:395:5:395:6 | xs | test.cpp:395:5:395:13 | Store: ... = ... | -| test.cpp:406:3:406:25 | ... = ... | test.cpp:406:7:406:8 | val indirection [post update] [xs] | -| test.cpp:406:7:406:8 | val indirection [post update] [xs] | test.cpp:407:3:407:5 | val indirection [xs] | -| test.cpp:406:12:406:25 | new[] | test.cpp:406:3:406:25 | ... = ... | -| test.cpp:407:3:407:5 | val indirection [xs] | test.cpp:407:7:407:8 | xs indirection | -| test.cpp:407:7:407:8 | xs indirection | test.cpp:407:7:407:8 | xs | -| test.cpp:417:16:417:33 | new[] | test.cpp:419:7:419:8 | xs | -| test.cpp:427:14:427:27 | new[] | test.cpp:433:5:433:6 | xs | -| test.cpp:439:14:439:27 | new[] | test.cpp:444:5:444:6 | xs | -| test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:6 | xs | -| test.cpp:455:5:455:6 | xs | test.cpp:455:5:455:15 | access to array | -| test.cpp:455:5:455:15 | access to array | test.cpp:455:5:455:19 | Store: ... = ... | -| test.cpp:461:14:461:27 | new[] | test.cpp:466:5:466:6 | xs | -| test.cpp:466:5:466:6 | xs | test.cpp:466:5:466:15 | access to array | -| test.cpp:466:5:466:15 | access to array | test.cpp:466:5:466:19 | Store: ... = ... | -| test.cpp:472:14:472:27 | new[] | test.cpp:477:5:477:6 | xs | -| test.cpp:483:14:483:27 | new[] | test.cpp:488:5:488:6 | xs | -| test.cpp:494:14:494:31 | new[] | test.cpp:499:5:499:6 | xs | -| test.cpp:505:14:505:31 | new[] | test.cpp:510:5:510:6 | xs | -| test.cpp:516:14:516:31 | new[] | test.cpp:521:5:521:6 | xs | -| test.cpp:527:14:527:31 | new[] | test.cpp:532:5:532:6 | xs | -| test.cpp:538:14:538:31 | new[] | test.cpp:543:5:543:6 | xs | -| test.cpp:549:14:549:31 | new[] | test.cpp:554:5:554:6 | xs | -| test.cpp:554:5:554:6 | xs | test.cpp:554:5:554:15 | access to array | -| test.cpp:554:5:554:15 | access to array | test.cpp:554:5:554:19 | Store: ... = ... | +| test.cpp:388:14:388:27 | new[] | test.cpp:392:3:392:4 | xs | +| test.cpp:399:14:399:27 | new[] | test.cpp:400:16:400:17 | xs | +| test.cpp:399:14:399:27 | new[] | test.cpp:402:5:402:6 | xs | +| test.cpp:410:14:410:27 | new[] | test.cpp:411:16:411:17 | xs | +| test.cpp:410:14:410:27 | new[] | test.cpp:413:5:413:6 | xs | +| test.cpp:411:15:411:23 | & ... | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:412:12:412:14 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:414:14:414:16 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:414:14:414:16 | end | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:15:411:23 | & ... | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:17 | xs | test.cpp:411:16:411:23 | access to array | +| test.cpp:411:16:411:17 | xs | test.cpp:411:16:411:23 | access to array | +| test.cpp:411:16:411:17 | xs | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:411:16:411:17 | xs | test.cpp:414:9:414:10 | xs | +| test.cpp:411:16:411:17 | xs | test.cpp:414:14:414:16 | end | +| test.cpp:411:16:411:17 | xs | test.cpp:415:7:415:11 | access to array | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:411:15:411:23 | & ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:412:12:412:14 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:414:14:414:16 | end | +| test.cpp:411:16:411:23 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:411:16:411:23 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:412:12:412:14 | end | test.cpp:414:14:414:16 | end | +| test.cpp:412:12:412:14 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:412:12:412:14 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:413:5:413:8 | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:414:9:414:10 | xs | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:414:9:414:10 | xs | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:413:5:413:8 | ... ++ | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:414:9:414:10 | xs | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:414:14:414:16 | end | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:415:7:415:11 | access to array | test.cpp:415:7:415:15 | Store: ... = ... | +| test.cpp:421:14:421:27 | new[] | test.cpp:422:16:422:17 | xs | +| test.cpp:421:14:421:27 | new[] | test.cpp:424:5:424:6 | xs | +| test.cpp:422:15:422:23 | & ... | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:423:12:423:14 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:425:18:425:20 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:425:18:425:20 | end | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:15:422:23 | & ... | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:17 | xs | test.cpp:422:16:422:23 | access to array | +| test.cpp:422:16:422:17 | xs | test.cpp:422:16:422:23 | access to array | +| test.cpp:422:16:422:17 | xs | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:422:16:422:17 | xs | test.cpp:425:9:425:10 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:425:9:425:10 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:425:18:425:20 | end | +| test.cpp:422:16:422:17 | xs | test.cpp:426:7:426:8 | xs | +| test.cpp:422:16:422:17 | xs | test.cpp:426:7:426:11 | access to array | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:422:15:422:23 | & ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:423:12:423:14 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:425:18:425:20 | end | +| test.cpp:422:16:422:23 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:422:16:422:23 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:423:12:423:14 | end | test.cpp:425:18:425:20 | end | +| test.cpp:423:12:423:14 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:423:12:423:14 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:424:5:424:8 | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:425:9:425:10 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:8 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:8 | xs | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:424:5:424:8 | ... ++ | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:8 | xs | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:9:425:10 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:425:18:425:20 | end | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:426:7:426:8 | xs | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:426:7:426:11 | access to array | test.cpp:426:7:426:15 | Store: ... = ... | +| test.cpp:432:14:432:27 | new[] | test.cpp:433:16:433:17 | xs | +| test.cpp:432:14:432:27 | new[] | test.cpp:436:5:436:6 | xs | +| test.cpp:433:15:433:23 | & ... | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:434:12:434:14 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:435:5:435:7 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:435:5:435:7 | end | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:15:433:23 | & ... | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:17 | xs | test.cpp:433:16:433:23 | access to array | +| test.cpp:433:16:433:17 | xs | test.cpp:433:16:433:23 | access to array | +| test.cpp:433:16:433:17 | xs | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:435:5:435:7 | end | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:433:16:433:17 | xs | test.cpp:437:9:437:10 | xs | +| test.cpp:433:16:433:17 | xs | test.cpp:438:7:438:11 | access to array | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:433:15:433:23 | & ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:434:12:434:14 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:435:5:435:7 | end | +| test.cpp:433:16:433:23 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:433:16:433:23 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:434:12:434:14 | end | test.cpp:435:5:435:7 | end | +| test.cpp:434:12:434:14 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:434:12:434:14 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:435:5:435:7 | end | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:436:5:436:8 | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:437:9:437:10 | xs | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:437:9:437:10 | xs | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:436:5:436:8 | ... ++ | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:437:9:437:10 | xs | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:438:7:438:11 | access to array | test.cpp:438:7:438:15 | Store: ... = ... | +| test.cpp:444:14:444:27 | new[] | test.cpp:445:16:445:17 | xs | +| test.cpp:444:14:444:27 | new[] | test.cpp:448:5:448:6 | xs | +| test.cpp:445:15:445:23 | & ... | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:446:3:446:5 | end | +| test.cpp:445:15:445:23 | & ... | test.cpp:446:3:446:5 | end | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:15:445:23 | & ... | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:17 | xs | test.cpp:445:16:445:23 | access to array | +| test.cpp:445:16:445:17 | xs | test.cpp:445:16:445:23 | access to array | +| test.cpp:445:16:445:17 | xs | test.cpp:446:3:446:5 | end | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:445:16:445:17 | xs | test.cpp:449:9:449:10 | xs | +| test.cpp:445:16:445:17 | xs | test.cpp:450:7:450:11 | access to array | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:445:15:445:23 | & ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:446:3:446:5 | end | +| test.cpp:445:16:445:23 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:445:16:445:23 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:446:3:446:5 | end | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:448:5:448:8 | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:449:9:449:10 | xs | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:449:9:449:10 | xs | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:448:5:448:8 | ... ++ | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:449:9:449:10 | xs | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:450:7:450:11 | access to array | test.cpp:450:7:450:15 | Store: ... = ... | +| test.cpp:456:14:456:31 | new[] | test.cpp:457:16:457:17 | xs | +| test.cpp:456:14:456:31 | new[] | test.cpp:460:5:460:6 | xs | +| test.cpp:468:14:468:27 | new[] | test.cpp:469:16:469:17 | xs | +| test.cpp:468:14:468:27 | new[] | test.cpp:472:5:472:6 | xs | +| test.cpp:480:14:480:27 | new[] | test.cpp:481:16:481:17 | xs | +| test.cpp:480:14:480:27 | new[] | test.cpp:484:5:484:6 | xs | +| test.cpp:481:15:481:23 | & ... | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:482:3:482:5 | end | +| test.cpp:481:15:481:23 | & ... | test.cpp:482:3:482:5 | end | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:15:481:23 | & ... | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:17 | xs | test.cpp:481:16:481:23 | access to array | +| test.cpp:481:16:481:17 | xs | test.cpp:481:16:481:23 | access to array | +| test.cpp:481:16:481:17 | xs | test.cpp:482:3:482:5 | end | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:481:16:481:17 | xs | test.cpp:485:9:485:10 | xs | +| test.cpp:481:16:481:17 | xs | test.cpp:486:7:486:11 | access to array | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:481:15:481:23 | & ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:482:3:482:5 | end | +| test.cpp:481:16:481:23 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:481:16:481:23 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:482:3:482:5 | end | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:484:5:484:8 | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:485:9:485:10 | xs | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:485:9:485:10 | xs | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:484:5:484:8 | ... ++ | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:485:9:485:10 | xs | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:486:7:486:11 | access to array | test.cpp:486:7:486:15 | Store: ... = ... | +| test.cpp:499:3:499:25 | ... = ... | test.cpp:499:7:499:8 | val indirection [post update] [xs] | +| test.cpp:499:7:499:8 | val indirection [post update] [xs] | test.cpp:500:3:500:5 | val indirection [xs] | +| test.cpp:499:12:499:25 | new[] | test.cpp:499:3:499:25 | ... = ... | +| test.cpp:500:3:500:5 | val indirection [xs] | test.cpp:500:7:500:8 | xs indirection | +| test.cpp:500:7:500:8 | xs indirection | test.cpp:500:7:500:8 | xs | +| test.cpp:510:16:510:33 | new[] | test.cpp:512:7:512:8 | xs | +| test.cpp:520:14:520:27 | new[] | test.cpp:526:5:526:6 | xs | +| test.cpp:532:14:532:27 | new[] | test.cpp:537:5:537:6 | xs | +| test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:6 | xs | +| test.cpp:548:5:548:6 | xs | test.cpp:548:5:548:15 | access to array | +| test.cpp:548:5:548:15 | access to array | test.cpp:548:5:548:19 | Store: ... = ... | +| test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:6 | xs | +| test.cpp:559:5:559:6 | xs | test.cpp:559:5:559:15 | access to array | +| test.cpp:559:5:559:15 | access to array | test.cpp:559:5:559:19 | Store: ... = ... | +| test.cpp:565:14:565:27 | new[] | test.cpp:570:5:570:6 | xs | +| test.cpp:576:14:576:27 | new[] | test.cpp:581:5:581:6 | xs | +| test.cpp:587:14:587:31 | new[] | test.cpp:592:5:592:6 | xs | +| test.cpp:598:14:598:31 | new[] | test.cpp:603:5:603:6 | xs | +| test.cpp:609:14:609:31 | new[] | test.cpp:614:5:614:6 | xs | +| test.cpp:620:14:620:31 | new[] | test.cpp:625:5:625:6 | xs | +| test.cpp:631:14:631:31 | new[] | test.cpp:636:5:636:6 | xs | +| test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:6 | xs | +| test.cpp:647:5:647:6 | xs | test.cpp:647:5:647:15 | access to array | +| test.cpp:647:5:647:15 | access to array | test.cpp:647:5:647:19 | Store: ... = ... | 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 | @@ -1125,11 +1348,6 @@ nodes | 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 | | test.cpp:377:14:377:27 | new[] | semmle.label | new[] | | test.cpp:378:15:378:16 | xs | semmle.label | xs | | test.cpp:378:15:378:23 | ... + ... | semmle.label | ... + ... | @@ -1143,53 +1361,147 @@ nodes | test.cpp:384:14:384:16 | end | semmle.label | end | | test.cpp:388:14:388:27 | new[] | semmle.label | new[] | | test.cpp:389:16:389:17 | xs | semmle.label | xs | -| test.cpp:392:5:392:6 | xs | semmle.label | xs | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:392:5:392:8 | ... ++ | semmle.label | ... ++ | -| test.cpp:393:9:393:10 | xs | semmle.label | xs | -| test.cpp:393:9:393:10 | xs | semmle.label | xs | -| test.cpp:395:5:395:6 | xs | semmle.label | xs | -| test.cpp:395:5:395:13 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:406:3:406:25 | ... = ... | semmle.label | ... = ... | -| test.cpp:406:7:406:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | -| test.cpp:406:12:406:25 | new[] | semmle.label | new[] | -| test.cpp:407:3:407:5 | val indirection [xs] | semmle.label | val indirection [xs] | -| test.cpp:407:7:407:8 | xs | semmle.label | xs | -| test.cpp:407:7:407:8 | xs indirection | semmle.label | xs indirection | -| test.cpp:417:16:417:33 | new[] | semmle.label | new[] | -| test.cpp:419:7:419:8 | xs | semmle.label | xs | -| test.cpp:427:14:427:27 | new[] | semmle.label | new[] | -| test.cpp:433:5:433:6 | xs | semmle.label | xs | -| test.cpp:439:14:439:27 | new[] | semmle.label | new[] | -| test.cpp:444:5:444:6 | xs | semmle.label | xs | -| test.cpp:450:14:450:27 | new[] | semmle.label | new[] | -| test.cpp:455:5:455:6 | xs | semmle.label | xs | -| test.cpp:455:5:455:15 | access to array | semmle.label | access to array | -| test.cpp:455:5:455:19 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:461:14:461:27 | new[] | semmle.label | new[] | -| test.cpp:466:5:466:6 | xs | semmle.label | xs | -| test.cpp:466:5:466:15 | access to array | semmle.label | access to array | -| test.cpp:466:5:466:19 | Store: ... = ... | semmle.label | Store: ... = ... | -| test.cpp:472:14:472:27 | new[] | semmle.label | new[] | -| test.cpp:477:5:477:6 | xs | semmle.label | xs | -| test.cpp:483:14:483:27 | new[] | semmle.label | new[] | -| test.cpp:488:5:488:6 | xs | semmle.label | xs | -| test.cpp:494:14:494:31 | new[] | semmle.label | new[] | -| test.cpp:499:5:499:6 | xs | semmle.label | xs | -| test.cpp:505:14:505:31 | new[] | semmle.label | new[] | -| test.cpp:510:5:510:6 | xs | semmle.label | xs | -| test.cpp:516:14:516:31 | new[] | semmle.label | new[] | -| test.cpp:521:5:521:6 | xs | semmle.label | xs | -| test.cpp:527:14:527:31 | new[] | semmle.label | new[] | -| test.cpp:532:5:532:6 | xs | semmle.label | xs | -| test.cpp:538:14:538:31 | new[] | semmle.label | new[] | -| test.cpp:543:5:543:6 | xs | semmle.label | xs | -| test.cpp:549:14:549:31 | new[] | semmle.label | new[] | -| test.cpp:554:5:554:6 | xs | semmle.label | xs | -| test.cpp:554:5:554:15 | access to array | semmle.label | access to array | -| test.cpp:554:5:554:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:392:3:392:4 | xs | semmle.label | xs | +| test.cpp:399:14:399:27 | new[] | semmle.label | new[] | +| test.cpp:400:16:400:17 | xs | semmle.label | xs | +| test.cpp:402:5:402:6 | xs | semmle.label | xs | +| test.cpp:410:14:410:27 | new[] | semmle.label | new[] | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:15:411:23 | & ... | semmle.label | & ... | +| test.cpp:411:16:411:17 | xs | semmle.label | xs | +| test.cpp:411:16:411:23 | access to array | semmle.label | access to array | +| test.cpp:411:16:411:23 | access to array | semmle.label | access to array | +| test.cpp:412:12:412:14 | end | semmle.label | end | +| test.cpp:412:12:412:14 | end | semmle.label | end | +| test.cpp:413:5:413:6 | xs | semmle.label | xs | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:413:5:413:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:414:9:414:10 | xs | semmle.label | xs | +| test.cpp:414:14:414:16 | end | semmle.label | end | +| test.cpp:415:7:415:11 | access to array | semmle.label | access to array | +| test.cpp:415:7:415:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:421:14:421:27 | new[] | semmle.label | new[] | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:15:422:23 | & ... | semmle.label | & ... | +| test.cpp:422:16:422:17 | xs | semmle.label | xs | +| test.cpp:422:16:422:23 | access to array | semmle.label | access to array | +| test.cpp:422:16:422:23 | access to array | semmle.label | access to array | +| test.cpp:423:12:423:14 | end | semmle.label | end | +| test.cpp:423:12:423:14 | end | semmle.label | end | +| test.cpp:424:5:424:6 | xs | semmle.label | xs | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:424:5:424:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:425:9:425:10 | xs | semmle.label | xs | +| test.cpp:425:9:425:10 | xs | semmle.label | xs | +| test.cpp:425:18:425:20 | end | semmle.label | end | +| test.cpp:426:7:426:8 | xs | semmle.label | xs | +| test.cpp:426:7:426:11 | access to array | semmle.label | access to array | +| test.cpp:426:7:426:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:432:14:432:27 | new[] | semmle.label | new[] | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:15:433:23 | & ... | semmle.label | & ... | +| test.cpp:433:16:433:17 | xs | semmle.label | xs | +| test.cpp:433:16:433:23 | access to array | semmle.label | access to array | +| test.cpp:433:16:433:23 | access to array | semmle.label | access to array | +| test.cpp:434:12:434:14 | end | semmle.label | end | +| test.cpp:434:12:434:14 | end | semmle.label | end | +| test.cpp:435:5:435:7 | end | semmle.label | end | +| test.cpp:436:5:436:6 | xs | semmle.label | xs | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:436:5:436:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:437:9:437:10 | xs | semmle.label | xs | +| test.cpp:438:7:438:11 | access to array | semmle.label | access to array | +| test.cpp:438:7:438:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:444:14:444:27 | new[] | semmle.label | new[] | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:15:445:23 | & ... | semmle.label | & ... | +| test.cpp:445:16:445:17 | xs | semmle.label | xs | +| test.cpp:445:16:445:23 | access to array | semmle.label | access to array | +| test.cpp:445:16:445:23 | access to array | semmle.label | access to array | +| test.cpp:446:3:446:5 | end | semmle.label | end | +| test.cpp:448:5:448:6 | xs | semmle.label | xs | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:448:5:448:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:449:9:449:10 | xs | semmle.label | xs | +| test.cpp:450:7:450:11 | access to array | semmle.label | access to array | +| test.cpp:450:7:450:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:456:14:456:31 | new[] | semmle.label | new[] | +| test.cpp:457:16:457:17 | xs | semmle.label | xs | +| test.cpp:460:5:460:6 | xs | semmle.label | xs | +| test.cpp:468:14:468:27 | new[] | semmle.label | new[] | +| test.cpp:469:16:469:17 | xs | semmle.label | xs | +| test.cpp:472:5:472:6 | xs | semmle.label | xs | +| test.cpp:480:14:480:27 | new[] | semmle.label | new[] | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:15:481:23 | & ... | semmle.label | & ... | +| test.cpp:481:16:481:17 | xs | semmle.label | xs | +| test.cpp:481:16:481:23 | access to array | semmle.label | access to array | +| test.cpp:481:16:481:23 | access to array | semmle.label | access to array | +| test.cpp:482:3:482:5 | end | semmle.label | end | +| test.cpp:484:5:484:6 | xs | semmle.label | xs | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:484:5:484:8 | ... ++ | semmle.label | ... ++ | +| test.cpp:485:9:485:10 | xs | semmle.label | xs | +| test.cpp:486:7:486:11 | access to array | semmle.label | access to array | +| test.cpp:486:7:486:15 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:499:3:499:25 | ... = ... | semmle.label | ... = ... | +| test.cpp:499:7:499:8 | val indirection [post update] [xs] | semmle.label | val indirection [post update] [xs] | +| test.cpp:499:12:499:25 | new[] | semmle.label | new[] | +| test.cpp:500:3:500:5 | val indirection [xs] | semmle.label | val indirection [xs] | +| test.cpp:500:7:500:8 | xs | semmle.label | xs | +| test.cpp:500:7:500:8 | xs indirection | semmle.label | xs indirection | +| test.cpp:510:16:510:33 | new[] | semmle.label | new[] | +| test.cpp:512:7:512:8 | xs | semmle.label | xs | +| test.cpp:520:14:520:27 | new[] | semmle.label | new[] | +| test.cpp:526:5:526:6 | xs | semmle.label | xs | +| test.cpp:532:14:532:27 | new[] | semmle.label | new[] | +| test.cpp:537:5:537:6 | xs | semmle.label | xs | +| test.cpp:543:14:543:27 | new[] | semmle.label | new[] | +| test.cpp:548:5:548:6 | xs | semmle.label | xs | +| test.cpp:548:5:548:15 | access to array | semmle.label | access to array | +| test.cpp:548:5:548:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:554:14:554:27 | new[] | semmle.label | new[] | +| test.cpp:559:5:559:6 | xs | semmle.label | xs | +| test.cpp:559:5:559:15 | access to array | semmle.label | access to array | +| test.cpp:559:5:559:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:565:14:565:27 | new[] | semmle.label | new[] | +| test.cpp:570:5:570:6 | xs | semmle.label | xs | +| test.cpp:576:14:576:27 | new[] | semmle.label | new[] | +| test.cpp:581:5:581:6 | xs | semmle.label | xs | +| test.cpp:587:14:587:31 | new[] | semmle.label | new[] | +| test.cpp:592:5:592:6 | xs | semmle.label | xs | +| test.cpp:598:14:598:31 | new[] | semmle.label | new[] | +| test.cpp:603:5:603:6 | xs | semmle.label | xs | +| test.cpp:609:14:609:31 | new[] | semmle.label | new[] | +| test.cpp:614:5:614:6 | xs | semmle.label | xs | +| test.cpp:620:14:620:31 | new[] | semmle.label | new[] | +| test.cpp:625:5:625:6 | xs | semmle.label | xs | +| test.cpp:631:14:631:31 | new[] | semmle.label | new[] | +| test.cpp:636:5:636:6 | xs | semmle.label | xs | +| test.cpp:642:14:642:31 | new[] | semmle.label | new[] | +| test.cpp:647:5:647:6 | xs | semmle.label | xs | +| test.cpp:647:5:647:15 | access to array | semmle.label | access to array | +| test.cpp:647:5:647:19 | Store: ... = ... | semmle.label | Store: ... = ... | 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 | @@ -1214,9 +1526,12 @@ subpaths | test.cpp:308:5:308:29 | Store: ... = ... | test.cpp:304:15:304:26 | new[] | test.cpp:308:5:308:29 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:304:15:304:26 | new[] | new[] | test.cpp:308:8:308:10 | ... + ... | ... + ... | | 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 | | test.cpp:384:13:384:16 | Load: * ... | test.cpp:377:14:377:27 | new[] | test.cpp:384:13:384:16 | Load: * ... | This read might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:377:14:377:27 | new[] | new[] | test.cpp:378:20:378:23 | size | size | -| test.cpp:395:5:395:13 | Store: ... = ... | test.cpp:388:14:388:27 | new[] | test.cpp:395:5:395:13 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:388:14:388:27 | new[] | new[] | test.cpp:389:19:389:22 | size | size | -| test.cpp:455:5:455:19 | Store: ... = ... | test.cpp:450:14:450:27 | new[] | test.cpp:455:5:455:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:450:14:450:27 | new[] | new[] | test.cpp:455:8:455:14 | src_pos | src_pos | -| test.cpp:466:5:466:19 | Store: ... = ... | test.cpp:461:14:461:27 | new[] | test.cpp:466:5:466:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:461:14:461:27 | new[] | new[] | test.cpp:466:8:466:14 | src_pos | src_pos | -| test.cpp:554:5:554:19 | Store: ... = ... | test.cpp:549:14:549:31 | new[] | test.cpp:554:5:554:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:549:14:549:31 | new[] | new[] | test.cpp:554:8:554:14 | src_pos | src_pos | +| test.cpp:415:7:415:15 | Store: ... = ... | test.cpp:410:14:410:27 | new[] | test.cpp:415:7:415:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:410:14:410:27 | new[] | new[] | test.cpp:411:19:411:22 | size | size | +| test.cpp:426:7:426:15 | Store: ... = ... | test.cpp:421:14:421:27 | new[] | test.cpp:426:7:426:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:421:14:421:27 | new[] | new[] | test.cpp:422:19:422:22 | size | size | +| test.cpp:438:7:438:15 | Store: ... = ... | test.cpp:432:14:432:27 | new[] | test.cpp:438:7:438:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:432:14:432:27 | new[] | new[] | test.cpp:433:19:433:22 | size | size | +| test.cpp:450:7:450:15 | Store: ... = ... | test.cpp:444:14:444:27 | new[] | test.cpp:450:7:450:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:444:14:444:27 | new[] | new[] | test.cpp:445:19:445:22 | size | size | +| test.cpp:486:7:486:15 | Store: ... = ... | test.cpp:480:14:480:27 | new[] | test.cpp:486:7:486:15 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 498. | test.cpp:480:14:480:27 | new[] | new[] | test.cpp:481:19:481:22 | size | size | +| test.cpp:548:5:548:19 | Store: ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos | +| test.cpp:559:5:559:19 | Store: ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos | +| test.cpp:647:5:647:19 | Store: ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos | 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 95af0938286..bdd3863544f 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 @@ -387,12 +387,105 @@ void test27(unsigned size, bool b) { void test28(unsigned size) { char *xs = new char[size]; char *end = &xs[size]; - if (xs >= end) - return; + if (xs >= end) + return; + xs++; + if (xs >= end) + return; + xs[0] = 0; // GOOD +} + +void test28_simple(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { xs++; - if (xs >= end) - return; - xs[0] = 0; // GOOD [FALSE POSITIVE] + if (xs < end) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple2(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + xs++; + if (xs < end + 1) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple3(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + xs++; + if (xs - 1 < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple4(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs < end) { + end++; + xs++; + if (xs < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple5(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end) { + xs[0] = 0; // BAD + } + } +} + +void test28_simple6(unsigned size) { + char *xs = new char[size + 1]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple7(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end++; + if (xs < end) { + xs++; + if (xs < end - 1) { + xs[0] = 0; // GOOD + } + } +} + +void test28_simple8(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + end += 500; + if (xs < end) { + xs++; + if (xs < end - 1) { + xs[0] = 0; // BAD + } + } } struct test29_struct { @@ -429,7 +522,7 @@ void test31(unsigned size, unsigned src_pos) src_pos = size; } unsigned dst_pos = src_pos; - if(dst_pos < size - 3) { + if (dst_pos < size - 3) { xs[dst_pos++] = 0; // GOOD } } @@ -440,7 +533,7 @@ void test31_simple1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size) { + if (src_pos < size) { xs[src_pos] = 0; // GOOD } } @@ -451,7 +544,7 @@ void test31_simple2(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size + 1) { + if (src_pos < size + 1) { xs[src_pos] = 0; // BAD } } @@ -462,7 +555,7 @@ void test31_simple3(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos - 1 < size) { + if (src_pos - 1 < size) { xs[src_pos] = 0; // BAD } } @@ -473,7 +566,7 @@ void test31_simple4(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size - 1) { + if (src_pos < size - 1) { xs[src_pos] = 0; // GOOD } } @@ -484,7 +577,7 @@ void test31_simple5(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos + 1 < size) { + if (src_pos + 1 < size) { xs[src_pos] = 0; // GOOD } } @@ -495,7 +588,7 @@ void test31_simple1_plus1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size) { + if (src_pos < size) { xs[src_pos] = 0; // GOOD } } @@ -506,7 +599,7 @@ void test31_simple2_plus1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size + 1) { + if (src_pos < size + 1) { xs[src_pos] = 0; // GOOD } } @@ -517,7 +610,7 @@ void test31_simple3_plus1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos - 1 < size) { + if (src_pos - 1 < size) { xs[src_pos] = 0; // GOOD } } @@ -528,7 +621,7 @@ void test31_simple4_plus1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size - 1) { + if (src_pos < size - 1) { xs[src_pos] = 0; // GOOD } } @@ -539,7 +632,7 @@ void test31_simple5_plus1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos + 1 < size) { + if (src_pos + 1 < size) { xs[src_pos] = 0; // GOOD } } @@ -550,7 +643,7 @@ void test31_simple1_sub1(unsigned size, unsigned src_pos) if (src_pos > size) { src_pos = size; } - if(src_pos < size) { + if (src_pos < size) { xs[src_pos] = 0; // BAD } } From a50d804ad710e0e314afc823ae2c10f334be8655 Mon Sep 17 00:00:00 2001 From: Ian Lynagh Date: Wed, 28 Jun 2023 13:48:43 +0100 Subject: [PATCH 76/93] Kotlin: Remove a use of ObsoleteDescriptorBasedAPI This isn't supported in Kotlin 2 mode, but removing this code doesn't affect any tests. --- .../src/main/kotlin/KotlinFileExtractor.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt index 6e5d921a406..ef7fafc913a 100644 --- a/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt +++ b/java/kotlin-extractor/src/main/kotlin/KotlinFileExtractor.kt @@ -157,21 +157,10 @@ open class KotlinFileExtractor( else -> false } - @OptIn(ObsoleteDescriptorBasedAPI::class) private fun isFake(d: IrDeclarationWithVisibility): Boolean { val hasFakeVisibility = d.visibility.let { it is DelegatedDescriptorVisibility && it.delegate == Visibilities.InvisibleFake } || d.isFakeOverride if (hasFakeVisibility && !isJavaBinaryObjectMethodRedeclaration(d)) return true - try { - if ((d as? IrFunction)?.descriptor?.isHiddenToOvercomeSignatureClash == true) { - return true - } - } - catch (e: NotImplementedError) { - // `org.jetbrains.kotlin.ir.descriptors.IrBasedClassConstructorDescriptor.isHiddenToOvercomeSignatureClash` throws the exception - logger.warnElement("Couldn't query if element is fake, deciding it's not.", d, e) - return false - } return false } From 25be9d48bceddf24ca97961d4ffcb9085250af05 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 28 Jun 2023 14:37:04 +0100 Subject: [PATCH 77/93] C++: Add tests with FPs that need call contexts. --- .../dataflow/dataflow-tests/test.cpp | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index 7b6ea0fa718..e481f76568d 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -734,3 +734,58 @@ void test_does_not_write_source_to_dereference() does_not_write_source_to_dereference(&x); sink(x); // $ ast,ir=733:7 SPURIOUS: ast,ir=726:11 } + +void sometimes_calls_sink_eq(int x, int n) { + if(n == 0) { + sink(x); // $ ast,ir=751:27 ast,ir=755:32 SPURIOUS: ast,ir=749:27 ast,ir=753:32 + } +} + +void call_sometimes_calls_sink_eq(int x, int n) { + sometimes_calls_sink_eq(x, n); +} + +void test_sometimes_calls_sink_eq_1() { + sometimes_calls_sink_eq(source(), 1); + sometimes_calls_sink_eq(0, 0); + sometimes_calls_sink_eq(source(), 0); + + call_sometimes_calls_sink_eq(source(), 1); + call_sometimes_calls_sink_eq(0, 0); + call_sometimes_calls_sink_eq(source(), 0); +} + +void sometimes_calls_sink_lt(int x, int n) { + if(n < 10) { + sink(x); // $ ast,ir=771:27 ast,ir=775:32 SPURIOUS: ast,ir=769:27 ast,ir=773:32 + } +} + +void call_sometimes_calls_sink_lt(int x, int n) { + sometimes_calls_sink_lt(x, n); +} + +void test_sometimes_calls_sink_lt() { + sometimes_calls_sink_lt(source(), 10); + sometimes_calls_sink_lt(0, 0); + sometimes_calls_sink_lt(source(), 2); + + call_sometimes_calls_sink_lt(source(), 10); + call_sometimes_calls_sink_lt(0, 0); + call_sometimes_calls_sink_lt(source(), 2); + +} + +void sometimes_calls_sink_switch(int x, int n) { + switch(n) { + case 0: + sink(x); // $ ast,ir=790:31 SPURIOUS: ast,ir=788:31 + break; + } +} + +void test_sometimes_calls_sink_switch() { + sometimes_calls_sink_switch(source(), 1); + sometimes_calls_sink_switch(0, 0); + sometimes_calls_sink_switch(source(), 0); +} \ No newline at end of file From 9e82ce8a138ce6f3e46b3d890c14fb7a41bb9a62 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 28 Jun 2023 14:37:35 +0100 Subject: [PATCH 78/93] C++: Implement 'isUnreachableInCall'. --- .../ir/dataflow/internal/DataFlowPrivate.qll | 68 ++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll index 31915a888ac..ec42989c649 100644 --- a/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll +++ b/cpp/ql/lib/semmle/code/cpp/ir/dataflow/internal/DataFlowPrivate.qll @@ -854,7 +854,73 @@ class DataFlowCall extends CallInstruction { Function getEnclosingCallable() { result = this.getEnclosingFunction() } } -predicate isUnreachableInCall(Node n, DataFlowCall call) { none() } // stub implementation +module IsUnreachableInCall { + private import semmle.code.cpp.ir.ValueNumbering + private import semmle.code.cpp.controlflow.IRGuards as G + + private class ConstantIntegralTypeArgumentNode extends PrimaryArgumentNode { + int value; + + ConstantIntegralTypeArgumentNode() { + value = op.getDef().(IntegerConstantInstruction).getValue().toInt() + } + + int getValue() { result = value } + } + + pragma[nomagic] + private predicate ensuresEq(Operand left, Operand right, int k, IRBlock block, boolean areEqual) { + any(G::IRGuardCondition guard).ensuresEq(left, right, k, block, areEqual) + } + + pragma[nomagic] + private predicate ensuresLt(Operand left, Operand right, int k, IRBlock block, boolean areEqual) { + any(G::IRGuardCondition guard).ensuresLt(left, right, k, block, areEqual) + } + + predicate isUnreachableInCall(Node n, DataFlowCall call) { + exists( + DirectParameterNode paramNode, ConstantIntegralTypeArgumentNode arg, + IntegerConstantInstruction constant, int k, Operand left, Operand right, IRBlock block + | + // arg flows into `paramNode` + DataFlowImplCommon::viableParamArg(call, paramNode, arg) and + left = constant.getAUse() and + right = valueNumber(paramNode.getInstruction()).getAUse() and + block = n.getBasicBlock() + | + // and there's a guard condition which ensures that the result of `left == right + k` is `areEqual` + exists(boolean areEqual | + ensuresEq(pragma[only_bind_into](left), pragma[only_bind_into](right), + pragma[only_bind_into](k), pragma[only_bind_into](block), areEqual) + | + // this block ensures that left = right + k, but it holds that `left != right + k` + areEqual = true and + constant.getValue().toInt() != arg.getValue() + k + or + // this block ensures that or `left != right + k`, but it holds that `left = right + k` + areEqual = false and + constant.getValue().toInt() = arg.getValue() + k + ) + or + // or there's a guard condition which ensures that the result of `left < right + k` is `isLessThan` + exists(boolean isLessThan | + ensuresLt(pragma[only_bind_into](left), pragma[only_bind_into](right), + pragma[only_bind_into](k), pragma[only_bind_into](block), isLessThan) + | + isLessThan = true and + // this block ensures that `left < right + k`, but it holds that `left >= right + k` + constant.getValue().toInt() >= arg.getValue() + k + or + // this block ensures that `left >= right + k`, but it holds that `left < right + k` + isLessThan = false and + constant.getValue().toInt() < arg.getValue() + k + ) + ) + } +} + +import IsUnreachableInCall int accessPathLimit() { result = 5 } From 655f1feac0f26f02647c6e30c9bd8e2d5b7e1bfc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 28 Jun 2023 14:40:50 +0100 Subject: [PATCH 79/93] C++: Accept test changes. --- cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index e481f76568d..347afddb4bd 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -737,7 +737,7 @@ void test_does_not_write_source_to_dereference() void sometimes_calls_sink_eq(int x, int n) { if(n == 0) { - sink(x); // $ ast,ir=751:27 ast,ir=755:32 SPURIOUS: ast,ir=749:27 ast,ir=753:32 + sink(x); // $ ast,ir=751:27 ast,ir=755:32 SPURIOUS: ast=749:27 ast,ir=753:32 // IR spurious results because we only have call contexts of depth 1 } } @@ -757,7 +757,7 @@ void test_sometimes_calls_sink_eq_1() { void sometimes_calls_sink_lt(int x, int n) { if(n < 10) { - sink(x); // $ ast,ir=771:27 ast,ir=775:32 SPURIOUS: ast,ir=769:27 ast,ir=773:32 + sink(x); // $ ast,ir=771:27 ast,ir=775:32 SPURIOUS: ast=769:27 ast,ir=773:32 // IR spurious results because we only have call contexts of depth 1 } } @@ -779,7 +779,7 @@ void test_sometimes_calls_sink_lt() { void sometimes_calls_sink_switch(int x, int n) { switch(n) { case 0: - sink(x); // $ ast,ir=790:31 SPURIOUS: ast,ir=788:31 + sink(x); // $ ast,ir=790:31 SPURIOUS: ast,ir=788:31 // IR spurious results because IRGuard don't understand switch statements. break; } } From 9d8ae5039a9e2d56b3771cf0501da9567f89bae3 Mon Sep 17 00:00:00 2001 From: jorgectf Date: Wed, 28 Jun 2023 17:53:56 +0200 Subject: [PATCH 80/93] Add models for `javax.portlet` --- ...6-28-javax-portlet-autogenerated-models.md | 4 + .../lib/ext/generated/javax.portlet.model.yml | 190 ++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md create mode 100644 java/ql/lib/ext/generated/javax.portlet.model.yml diff --git a/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md b/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md new file mode 100644 index 00000000000..7e6e88f7595 --- /dev/null +++ b/java/ql/lib/change-notes/2023-06-28-javax-portlet-autogenerated-models.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added automatically-generated dataflow models for `javax.portlet`. diff --git a/java/ql/lib/ext/generated/javax.portlet.model.yml b/java/ql/lib/ext/generated/javax.portlet.model.yml new file mode 100644 index 00000000000..085d5f4800a --- /dev/null +++ b/java/ql/lib/ext/generated/javax.portlet.model.yml @@ -0,0 +1,190 @@ +# THIS FILE IS AN AUTO-GENERATED MODELS AS DATA FILE. DO NOT EDIT. +# Definitions of models for the Java Portlet framework. +extensions: + - addsTo: + pack: codeql/java-all + extensible: summaryModel + data: + - ["javax.portlet.filter", "ActionRequestWrapper", true, "ActionRequestWrapper", "(ActionRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ActionRequestWrapper", true, "setRequest", "(ActionRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ActionResponseWrapper", true, "ActionResponseWrapper", "(ActionResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ActionResponseWrapper", true, "setResponse", "(ActionResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "EventRequestWrapper", true, "EventRequestWrapper", "(EventRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "EventRequestWrapper", true, "setRequest", "(EventRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "EventResponseWrapper", true, "EventResponseWrapper", "(EventResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "EventResponseWrapper", true, "setResponse", "(EventResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletRequestWrapper", true, "PortletRequestWrapper", "(PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletRequestWrapper", true, "getRequest", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletRequestWrapper", true, "setRequest", "(PortletRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletResponseWrapper", true, "PortletResponseWrapper", "(PortletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletResponseWrapper", true, "getResponse", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet.filter", "PortletResponseWrapper", true, "setResponse", "(PortletResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "RenderRequestWrapper", true, "RenderRequestWrapper", "(RenderRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "RenderRequestWrapper", true, "setRequest", "(RenderRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "RenderResponseWrapper", true, "RenderResponseWrapper", "(RenderResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "RenderResponseWrapper", true, "setResponse", "(RenderResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ResourceRequestWrapper", true, "ResourceRequestWrapper", "(ResourceRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ResourceRequestWrapper", true, "setRequest", "(ResourceRequest)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ResourceResponseWrapper", true, "ResourceResponseWrapper", "(ResourceResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet.filter", "ResourceResponseWrapper", true, "setResponse", "(ResourceResponse)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "GenericPortlet", true, "getPortletConfig", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "Portlet", true, "init", "(PortletConfig)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletException", true, "PortletException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletException", true, "PortletException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletException", true, "PortletException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletMode", true, "PortletMode", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletMode", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(String,PortletMode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(String,PortletMode)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(String,Throwable,PortletMode)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(String,Throwable,PortletMode)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(String,Throwable,PortletMode)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "PortletModeException", "(Throwable,PortletMode)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletModeException", true, "getMode", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "PortletSecurityException", true, "PortletSecurityException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletSecurityException", true, "PortletSecurityException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletSecurityException", true, "PortletSecurityException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "PortletSessionUtil", true, "decodeAttributeName", "(String)", "", "Argument[0]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "ReadOnlyException", true, "ReadOnlyException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ReadOnlyException", true, "ReadOnlyException", "(String,Throwable)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ReadOnlyException", true, "ReadOnlyException", "(String,Throwable)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "UnavailableException", true, "UnavailableException", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "UnavailableException", true, "UnavailableException", "(String,int)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(String,Collection)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(String,Collection)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(String,Throwable,Collection)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(String,Throwable,Collection)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(String,Throwable,Collection)", "", "Argument[2].Element", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "ValidatorException", "(Throwable,Collection)", "", "Argument[1].Element", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "ValidatorException", true, "getFailedKeys", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "WindowState", true, "WindowState", "(String)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowState", true, "toString", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(String,Throwable,WindowState)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(String,Throwable,WindowState)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(String,Throwable,WindowState)", "", "Argument[2]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(String,WindowState)", "", "Argument[0]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(String,WindowState)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "WindowStateException", "(Throwable,WindowState)", "", "Argument[1]", "Argument[this]", "taint", "df-generated"] + - ["javax.portlet", "WindowStateException", true, "getState", "()", "", "Argument[this]", "ReturnValue", "taint", "df-generated"] + - addsTo: + pack: codeql/java-all + extensible: neutralModel + data: + - ["javax.portlet", "ActionResponse", "sendRedirect", "(String)", "summary", "df-generated"] + - ["javax.portlet", "ActionResponse", "sendRedirect", "(String,String)", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getCharacterEncoding", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getContentLength", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getContentType", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getMethod", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getPortletInputStream", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "getReader", "()", "summary", "df-generated"] + - ["javax.portlet", "ClientDataRequest", "setCharacterEncoding", "(String)", "summary", "df-generated"] + - ["javax.portlet", "EventPortlet", "processEvent", "(EventRequest,EventResponse)", "summary", "df-generated"] + - ["javax.portlet", "EventRequest", "getEvent", "()", "summary", "df-generated"] + - ["javax.portlet", "EventRequest", "getMethod", "()", "summary", "df-generated"] + - ["javax.portlet", "EventResponse", "setRenderParameters", "(EventRequest)", "summary", "df-generated"] + - ["javax.portlet", "GenericPortlet", "init", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "createActionURL", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "createRenderURL", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "createResourceURL", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "flushBuffer", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getBufferSize", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getCacheControl", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getCharacterEncoding", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getContentType", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getLocale", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getPortletOutputStream", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "getWriter", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "isCommitted", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "reset", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "resetBuffer", "()", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "setBufferSize", "(int)", "summary", "df-generated"] + - ["javax.portlet", "MimeResponse", "setContentType", "(String)", "summary", "df-generated"] + - ["javax.portlet", "Portlet", "destroy", "()", "summary", "df-generated"] + - ["javax.portlet", "Portlet", "processAction", "(ActionRequest,ActionResponse)", "summary", "df-generated"] + - ["javax.portlet", "Portlet", "render", "(RenderRequest,RenderResponse)", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getContainerRuntimeOptions", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getDefaultNamespace", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getInitParameter", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getInitParameterNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getPortletContext", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getPortletName", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getProcessingEventQNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getPublicRenderParameterNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getPublishingEventQNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getResourceBundle", "(Locale)", "summary", "df-generated"] + - ["javax.portlet", "PortletConfig", "getSupportedLocales", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletException", "PortletException", "(Throwable)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest$P3PUserInfos", "toString", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getAttribute", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getAttributeNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getAuthType", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getContextPath", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getCookies", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getLocale", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getLocales", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getParameter", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getParameterMap", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getParameterNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getParameterValues", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPortalContext", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPortletMode", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPortletSession", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPortletSession", "(boolean)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPreferences", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPrivateParameterMap", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getProperties", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getProperty", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPropertyNames", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getPublicParameterMap", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getRemoteUser", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getRequestedSessionId", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getResponseContentType", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getResponseContentTypes", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getScheme", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getServerName", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getServerPort", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getUserPrincipal", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getWindowID", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "getWindowState", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "isPortletModeAllowed", "(PortletMode)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "isRequestedSessionIdValid", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "isSecure", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "isUserInRole", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "isWindowStateAllowed", "(WindowState)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "removeAttribute", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletRequest", "setAttribute", "(String,Object)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "addProperty", "(Cookie)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "addProperty", "(String,Element)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "addProperty", "(String,String)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "createElement", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "encodeURL", "(String)", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "getNamespace", "()", "summary", "df-generated"] + - ["javax.portlet", "PortletResponse", "setProperty", "(String,String)", "summary", "df-generated"] + - ["javax.portlet", "PortletSecurityException", "PortletSecurityException", "(Throwable)", "summary", "df-generated"] + - ["javax.portlet", "PortletSessionUtil", "decodeScope", "(String)", "summary", "df-generated"] + - ["javax.portlet", "ReadOnlyException", "ReadOnlyException", "(Throwable)", "summary", "df-generated"] + - ["javax.portlet", "RenderRequest", "getETag", "()", "summary", "df-generated"] + - ["javax.portlet", "RenderResponse", "setNextPossiblePortletModes", "(Collection)", "summary", "df-generated"] + - ["javax.portlet", "RenderResponse", "setTitle", "(String)", "summary", "df-generated"] + - ["javax.portlet", "ResourceRequest", "getCacheability", "()", "summary", "df-generated"] + - ["javax.portlet", "ResourceRequest", "getETag", "()", "summary", "df-generated"] + - ["javax.portlet", "ResourceRequest", "getPrivateRenderParameterMap", "()", "summary", "df-generated"] + - ["javax.portlet", "ResourceRequest", "getResourceID", "()", "summary", "df-generated"] + - ["javax.portlet", "ResourceResponse", "setCharacterEncoding", "(String)", "summary", "df-generated"] + - ["javax.portlet", "ResourceResponse", "setContentLength", "(int)", "summary", "df-generated"] + - ["javax.portlet", "ResourceResponse", "setLocale", "(Locale)", "summary", "df-generated"] + - ["javax.portlet", "ResourceServingPortlet", "serveResource", "(ResourceRequest,ResourceResponse)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "getPortletMode", "()", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "getRenderParameterMap", "()", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "getWindowState", "()", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "removePublicRenderParameter", "(String)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setEvent", "(QName,Serializable)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setEvent", "(String,Serializable)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setPortletMode", "(PortletMode)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setRenderParameter", "(String,String)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setRenderParameter", "(String,String[])", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setRenderParameters", "(Map)", "summary", "df-generated"] + - ["javax.portlet", "StateAwareResponse", "setWindowState", "(WindowState)", "summary", "df-generated"] + - ["javax.portlet", "UnavailableException", "getUnavailableSeconds", "()", "summary", "df-generated"] + - ["javax.portlet", "UnavailableException", "isPermanent", "()", "summary", "df-generated"] From 527b908bdaa011554e0d00eef1b9b21fe7ca8909 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Wed, 28 Jun 2023 17:54:12 +0200 Subject: [PATCH 81/93] C++: Fix test annotation for `cpp/invalid-pointer-deref` test --- .../query-tests/Security/CWE/CWE-193/pointer-deref/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 bdd3863544f..b10a2775fb2 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 @@ -369,7 +369,7 @@ void test26(unsigned size) { } if (p < end) { - int val = *p; // GOOD [FALSE POSITIVE] + int val = *p; // GOOD } } From ede6b262cd3caa7360b103b1d52db2caf01a36b7 Mon Sep 17 00:00:00 2001 From: Alex Ford Date: Wed, 28 Jun 2023 17:09:43 +0100 Subject: [PATCH 82/93] Ruby: fix sqlite3 PreparedStatementExecution.getSql() predicate --- ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll | 2 +- ruby/ql/test/library-tests/frameworks/sqlite3/Sqlite3.expected | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index dfb93a0e6e5..981ace2e7da 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll @@ -47,7 +47,7 @@ module Sqlite3 { this.getMethodName() = ["columns", "execute", "execute!", "get_metadata", "types"] } - override DataFlow::Node getSql() { result = stmt.getReceiver() } + override DataFlow::Node getSql() { result = stmt.getSql() } } /** Gets the name of a method called against a database that executes an SQL statement. */ diff --git a/ruby/ql/test/library-tests/frameworks/sqlite3/Sqlite3.expected b/ruby/ql/test/library-tests/frameworks/sqlite3/Sqlite3.expected index bd4f9883045..9e7263aa3bb 100644 --- a/ruby/ql/test/library-tests/frameworks/sqlite3/Sqlite3.expected +++ b/ruby/ql/test/library-tests/frameworks/sqlite3/Sqlite3.expected @@ -5,6 +5,6 @@ sqlite3SqlConstruction | sqlite3.rb:29:7:29:40 | call to execute | sqlite3.rb:29:19:29:39 | "select * from table" | sqlite3SqlExecution | sqlite3.rb:5:1:5:17 | call to execute | sqlite3.rb:5:12:5:17 | <<-SQL | -| sqlite3.rb:14:1:14:12 | call to execute | sqlite3.rb:12:8:12:9 | db | +| sqlite3.rb:14:1:14:12 | call to execute | sqlite3.rb:12:19:12:41 | "select * from numbers" | | sqlite3.rb:17:3:19:5 | call to execute | sqlite3.rb:17:15:17:35 | "select * from table" | | sqlite3.rb:29:7:29:40 | call to execute | sqlite3.rb:29:19:29:39 | "select * from table" | From 6806b8750d2fd09e28ca382a65dd40403f8a9a76 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 29 Jun 2023 11:49:16 +0200 Subject: [PATCH 83/93] Java: Use getSourceDeclaration to handle generic types --- java/ql/src/Telemetry/ExternalApi.qll | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/ql/src/Telemetry/ExternalApi.qll b/java/ql/src/Telemetry/ExternalApi.qll index 04785a208a8..ed8dc3fa3eb 100644 --- a/java/ql/src/Telemetry/ExternalApi.qll +++ b/java/ql/src/Telemetry/ExternalApi.qll @@ -27,8 +27,9 @@ class ExternalApi extends Callable { */ string getApiName() { result = - this.getDeclaringType().getPackage() + "." + this.getDeclaringType().nestedName() + "#" + - this.getName() + paramsString(this) + this.getDeclaringType().getPackage() + "." + + this.getDeclaringType().getSourceDeclaration().nestedName() + "#" + this.getName() + + paramsString(this) } private string getJarName() { From dd548945c79a4520367538f083cbb15263635fb2 Mon Sep 17 00:00:00 2001 From: Kasper Svendsen Date: Thu, 29 Jun 2023 14:25:44 +0200 Subject: [PATCH 84/93] Enable checks for implicit this warnings --- .github/workflows/check-implicit-this.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check-implicit-this.yml b/.github/workflows/check-implicit-this.yml index 8539c7b3330..8711d7955bc 100644 --- a/.github/workflows/check-implicit-this.yml +++ b/.github/workflows/check-implicit-this.yml @@ -1,6 +1,13 @@ name: "Check implicit this warnings" -on: workflow_dispatch +on: + workflow_dispatch: + pull_request: + paths: + - "**qlpack.yml" + branches: + - main + - "rc/*" jobs: check: @@ -15,7 +22,7 @@ jobs: for pack_file in ${packs}; do option="$(yq '.warnOnImplicitThis' ${pack_file})" if [ "${option}" != "true" ]; then - echo "warnOnImplicitThis property must be set to 'true' for pack ${pack_file}" + echo "::error file=${pack_file}::warnOnImplicitThis property must be set to 'true' for pack ${pack_file}" EXIT_CODE=1 fi done From 2c8d4724d586d0840fb363b45c07a8054226b529 Mon Sep 17 00:00:00 2001 From: Jeroen Ketema Date: Thu, 29 Jun 2023 14:45:09 +0200 Subject: [PATCH 85/93] C++: Add more `cpp/invalid-pointer-deref` FP test cases --- .../InvalidPointerDeref.expected | 34 +++++++++++++++++++ .../CWE/CWE-193/pointer-deref/test.cpp | 28 +++++++++++++++ 2 files changed, 62 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 b1ecd38c3c3..32b068daf81 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 @@ -1022,6 +1022,25 @@ edges | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:6 | xs | | test.cpp:647:5:647:6 | xs | test.cpp:647:5:647:15 | access to array | | test.cpp:647:5:647:15 | access to array | test.cpp:647:5:647:19 | Store: ... = ... | +| test.cpp:652:14:652:27 | new[] | test.cpp:653:16:653:17 | xs | +| test.cpp:652:14:652:27 | new[] | test.cpp:656:3:656:4 | xs | +| test.cpp:653:16:653:17 | xs | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:653:16:653:17 | xs | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:653:16:653:17 | xs | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:653:16:653:17 | xs | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:653:16:653:17 | xs | test.cpp:657:7:657:8 | xs | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:656:3:656:6 | ... ++ | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:657:7:657:8 | xs | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:657:7:657:8 | xs | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | Store: ... = ... | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | Store: ... = ... | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | Store: ... = ... | +| test.cpp:656:3:656:6 | ... ++ | test.cpp:662:3:662:11 | Store: ... = ... | +| test.cpp:657:7:657:8 | xs | test.cpp:662:3:662:11 | Store: ... = ... | +| test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:8 | xs | +| test.cpp:675:7:675:8 | xs | test.cpp:675:7:675:19 | access to array | +| test.cpp:675:7:675:19 | access to array | test.cpp:675:7:675:23 | Store: ... = ... | 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 | @@ -1502,6 +1521,19 @@ nodes | test.cpp:647:5:647:6 | xs | semmle.label | xs | | test.cpp:647:5:647:15 | access to array | semmle.label | access to array | | test.cpp:647:5:647:19 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:652:14:652:27 | new[] | semmle.label | new[] | +| test.cpp:653:16:653:17 | xs | semmle.label | xs | +| test.cpp:656:3:656:4 | xs | semmle.label | xs | +| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ | +| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ | +| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ | +| test.cpp:656:3:656:6 | ... ++ | semmle.label | ... ++ | +| test.cpp:657:7:657:8 | xs | semmle.label | xs | +| test.cpp:662:3:662:11 | Store: ... = ... | semmle.label | Store: ... = ... | +| test.cpp:667:14:667:31 | new[] | semmle.label | new[] | +| test.cpp:675:7:675:8 | xs | semmle.label | xs | +| test.cpp:675:7:675:19 | access to array | semmle.label | access to array | +| test.cpp:675:7:675:23 | Store: ... = ... | semmle.label | Store: ... = ... | 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 | @@ -1535,3 +1567,5 @@ subpaths | test.cpp:548:5:548:19 | Store: ... = ... | test.cpp:543:14:543:27 | new[] | test.cpp:548:5:548:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:543:14:543:27 | new[] | new[] | test.cpp:548:8:548:14 | src_pos | src_pos | | test.cpp:559:5:559:19 | Store: ... = ... | test.cpp:554:14:554:27 | new[] | test.cpp:559:5:559:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:554:14:554:27 | new[] | new[] | test.cpp:559:8:559:14 | src_pos | src_pos | | test.cpp:647:5:647:19 | Store: ... = ... | test.cpp:642:14:642:31 | new[] | test.cpp:647:5:647:19 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:642:14:642:31 | new[] | new[] | test.cpp:647:8:647:14 | src_pos | src_pos | +| test.cpp:662:3:662:11 | Store: ... = ... | test.cpp:652:14:652:27 | new[] | test.cpp:662:3:662:11 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@ + 1. | test.cpp:652:14:652:27 | new[] | new[] | test.cpp:653:19:653:22 | size | size | +| test.cpp:675:7:675:23 | Store: ... = ... | test.cpp:667:14:667:31 | new[] | test.cpp:675:7:675:23 | Store: ... = ... | This write might be out of bounds, as the pointer might be equal to $@ + $@. | test.cpp:667:14:667:31 | new[] | new[] | test.cpp:675:10:675:18 | ... ++ | ... ++ | 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 b10a2775fb2..13e373bac10 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 @@ -647,3 +647,31 @@ void test31_simple1_sub1(unsigned size, unsigned src_pos) xs[src_pos] = 0; // BAD } } + +void test32(unsigned size) { + char *xs = new char[size]; + char *end = &xs[size]; + if (xs >= end) + return; + xs++; + if (xs >= end) + return; + xs++; + if (xs >= end) + return; + xs[0] = 0; // GOOD [FALSE POSITIVE] +} + +void test33(unsigned size, unsigned src_pos) +{ + char *xs = new char[size + 1]; + if (src_pos > size) { + src_pos = size; + } + unsigned dst_pos = src_pos; + while (dst_pos < size - 1) { + dst_pos++; + if (true) + xs[dst_pos++] = 0; // GOOD [FALSE POSITIVE] + } +} From 2de356dbfec782c484ddfa39e0564e6cc0dcc1e9 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Thu, 29 Jun 2023 15:06:43 +0200 Subject: [PATCH 86/93] C#: Remove the renaming of `Directory.Build.props` from standalone extraction --- .../Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs index f4f1cba4d89..c9d9694ad23 100644 --- a/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs +++ b/csharp/extractor/Semmle.Extraction.CSharp.Standalone/BuildAnalysis.cs @@ -100,9 +100,8 @@ namespace Semmle.BuildAnalyser dllDirNames.Add(runtimeLocation); } - // These files can sometimes prevent `dotnet restore` from working correctly. + // TODO: remove the below when the required SDK is installed using (new FileRenamer(sourceDir.GetFiles("global.json", SearchOption.AllDirectories))) - using (new FileRenamer(sourceDir.GetFiles("Directory.Build.props", SearchOption.AllDirectories))) { var solutions = options.SolutionFile is not null ? new[] { options.SolutionFile } : From 5d1a437e9cdbe417cfa0bd9d877801c3693305d6 Mon Sep 17 00:00:00 2001 From: Asger F Date: Thu, 29 Jun 2023 15:39:19 +0200 Subject: [PATCH 87/93] Revert "Ruby: overhaul API graphs" --- .../dataflow/new/internal/TypeTracker.qll | 45 +- .../2023-06-28-tracking-on-demand.md | 7 - ruby/ql/lib/codeql/ruby/ApiGraphs.qll | 1872 ++++++----------- .../ruby/dataflow/internal/DataFlowPublic.qll | 134 +- .../UnicodeBypassValidationQuery.qll | 8 +- .../experimental/ZipSlipCustomizations.qll | 2 +- .../ruby/frameworks/ActionController.qll | 47 +- .../codeql/ruby/frameworks/ActionMailbox.qll | 6 +- .../codeql/ruby/frameworks/ActionMailer.qll | 42 +- .../codeql/ruby/frameworks/ActiveRecord.qll | 265 ++- .../codeql/ruby/frameworks/ActiveResource.qll | 196 +- .../ql/lib/codeql/ruby/frameworks/GraphQL.qll | 25 +- .../ql/lib/codeql/ruby/frameworks/Sqlite3.qll | 25 +- ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll | 50 +- .../lib/codeql/ruby/frameworks/core/Gem.qll | 4 +- .../codeql/ruby/frameworks/core/Kernel.qll | 3 +- .../ruby/frameworks/data/ModelsAsData.qll | 2 +- .../data/internal/ApiGraphModelsSpecific.qll | 54 +- .../codeql/ruby/security/KernelOpenQuery.qll | 3 +- ruby/ql/lib/codeql/ruby/security/OpenSSL.qll | 4 +- .../ruby/typetracking/ApiGraphShared.qll | 328 --- .../codeql/ruby/typetracking/TypeTracker.qll | 45 +- .../dataflow/api-graphs/ApiGraphs.expected | 8 +- .../api-graphs/VerifyApiGraphExpectations.ql | 77 - .../dataflow/api-graphs/callbacks.rb | 52 +- .../dataflow/api-graphs/chained-access.rb | 31 - .../dataflow/api-graphs/method-callbacks.rb | 64 - .../dataflow/api-graphs/self-dot-class.rb | 10 - .../dataflow/api-graphs/test1.rb | 72 +- ...raphExpectations.expected => use.expected} | 0 .../library-tests/dataflow/api-graphs/use.ql | 88 + .../frameworks/Twirp/Twirp.expected | 2 - .../library-tests/frameworks/Twirp/Twirp.ql | 2 +- .../frameworks/Twirp/hello_world_server.rb | 15 +- .../action_dispatch/ActionDispatch.expected | 12 +- .../active_record/ActiveRecord.expected | 95 +- .../frameworks/active_record/ActiveRecord.ql | 14 +- .../active_resource/ActiveResource.expected | 10 - .../active_resource/ActiveResource.ql | 15 +- .../security/cwe-079/StoredXSS.expected | 10 + .../app/views/foo/stores/show.html.erb | 6 +- .../security/cwe-089/SqlInjection.expected | 9 +- .../util/test/InlineExpectationsTest.qll | 13 - 43 files changed, 1314 insertions(+), 2458 deletions(-) delete mode 100644 ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md delete mode 100644 ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll delete mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql delete mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb delete mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb delete mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb rename ruby/ql/test/library-tests/dataflow/api-graphs/{VerifyApiGraphExpectations.expected => use.expected} (100%) create mode 100644 ruby/ql/test/library-tests/dataflow/api-graphs/use.ql diff --git a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll index 001375b4dc5..fb3d7bf828f 100644 --- a/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll +++ b/python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll @@ -55,9 +55,10 @@ private module Cached { ) } - /** Gets a type tracker with no content and the call bit set to the given value. */ - cached - TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } + pragma[nomagic] + private TypeTracker noContentTypeTracker(boolean hasCall) { + result = MkTypeTracker(hasCall, noContent()) + } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -317,8 +318,6 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { - predicate append = Cached::append/2; - /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -379,35 +378,6 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; - - /** Gets the step summary for a level step. */ - StepSummary levelStep() { result = LevelStep() } - - /** Gets the step summary for a call step. */ - StepSummary callStep() { result = CallStep() } - - /** Gets the step summary for a return step. */ - StepSummary returnStep() { result = ReturnStep() } - - /** Gets the step summary for storing into `content`. */ - StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } - - /** Gets the step summary for loading from `content`. */ - StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } - - /** Gets the step summary for loading from `load` and then storing into `store`. */ - StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { - result = LoadStoreStep(load, store) - } - - /** Gets the step summary for a step that only permits contents matched by `filter`. */ - StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } - - /** Gets the step summary for a step that blocks contents matched by `filter`. */ - StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } - - /** Gets the step summary for a jump step. */ - StepSummary jumpStep() { result = JumpStep() } } /** @@ -570,13 +540,6 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } - - /** - * INTERNAL USE ONLY. - * - * Gets a valid end point of type tracking with the call bit set to the given value. - */ - predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] diff --git a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md b/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md deleted file mode 100644 index 5aa79d5e2f3..00000000000 --- a/ruby/ql/lib/change-notes/2023-06-28-tracking-on-demand.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -category: majorAnalysis ---- -* The API graph library (`codeql.ruby.ApiGraphs`) has been significantly improved, with better support for inheritance, - and data-flow nodes can now be converted to API nodes by calling `.track()` or `.backtrack()` on the node. - API graphs allow for efficient modelling of how a given value is used by the code base, or how values produced by the code base - are consumed by a library. See the documentation for `API::Node` for details and examples. diff --git a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll index 0d6e669b1de..1cf4c445781 100644 --- a/ruby/ql/lib/codeql/ruby/ApiGraphs.qll +++ b/ruby/ql/lib/codeql/ruby/ApiGraphs.qll @@ -1,13 +1,13 @@ /** - * Provides an implementation of _API graphs_, which allow efficient modelling of how a given - * value is used by the code base or how values produced by the code base are consumed by a library. + * Provides an implementation of _API graphs_, which are an abstract representation of the API + * surface used and/or defined by a code base. * - * See `API::Node` for more details. + * The nodes of the API graph represent definitions and uses of API components. The edges are + * directed and labeled; they specify how the components represented by nodes relate to each other. */ private import codeql.ruby.AST private import codeql.ruby.DataFlow -private import codeql.ruby.typetracking.ApiGraphShared private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.typetracking.TypeTrackerSpecific as TypeTrackerSpecific private import codeql.ruby.controlflow.CfgNodes @@ -19,140 +19,85 @@ private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatc */ module API { /** - * A node in the API graph, that is, a value that can be tracked interprocedurally. + * A node in the API graph, representing a value that has crossed the boundary between this + * codebase and an external library (or in general, any external codebase). * - * The API graph is a graph for tracking values of certain types in a way that accounts for inheritance - * and interprocedural data flow. + * ### Basic usage * * API graphs are typically used to identify "API calls", that is, calls to an external function * whose implementation is not necessarily part of the current codebase. * - * ### Basic usage - * * The most basic use of API graphs is typically as follows: * 1. Start with `API::getTopLevelMember` for the relevant library. * 2. Follow up with a chain of accessors such as `getMethod` describing how to get to the relevant API function. - * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource`, `asSink`, or `asCall`. + * 3. Map the resulting API graph nodes to data-flow nodes, using `asSource` or `asSink`. * - * The following examples demonstrate how to identify the expression `x` in various basic cases: - * ```rb - * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() - * Foo.bar(x) - * - * # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("foo").asSink() - * Foo.bar(foo: x) - * - * # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() - * Foo.new.bar(x) - * - * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() - * end - * ``` - * - * ### Data flow - * - * The members predicates on this class generally take inheritance and data flow into account. - * - * The following example demonstrates a case where data flow was used to find the sink `x`: - * ```ruby - * def doSomething f - * f.bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() - * end - * doSomething Foo.new - * ``` - * The call `API::getTopLevelMember("Foo").getInstance()` identifies the `Foo.new` call, and `getMethod("bar")` - * then follows data flow from there to find calls to `bar` where that object flows to the receiver. - * This results in the `f.bar` call. - * - * ### Backward data flow - * - * When inspecting the arguments of a call, the data flow direction is backwards. - * The following example illustrates this when we match the `x` parameter of a block: - * ```ruby - * def doSomething &blk - * Foo.bar &blk - * end - * doSomething do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0).asSource() - * end - * ``` - * When `getParameter(0)` is evaluated, the API graph backtracks the `&blk` argument to the block argument a few - * lines below. As a result, it eventually matches the `x` parameter of that block. - * - * ### Inheritance - * - * When a class or module object is tracked, inheritance is taken into account. - * - * In the following example, a call to `Foo.bar` was found via a subclass of `Foo`, - * because classes inherit singleton methods from their base class: - * ```ruby - * class Subclass < Foo - * def self.doSomething - * bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).asSink() - * end - * end - * ``` - * - * Similarly, instance methods can be found in subclasses, or ancestors of subclases in cases of multiple inheritance: - * ```rb - * module Mixin - * def doSomething - * bar(x) # API::getTopLevelMember("Foo").getInstance().getMethod("bar").getArgument(0).asSink() - * end - * end - * class Subclass < Foo - * include Mixin - * end - * ``` - * The value of `self` in `Mixin#doSomething` is seen as a potential instance of `Foo`, and is thus found by `getTopLevelMember("Foo").getInstance()`. - * This eventually results in finding the call `bar`, due to its implicit `self` receiver, and finally its argument `x` is found as the sink. - * - * ### Backward data flow and classes - * - * When inspecting the arguments of a call, and the value flowing into that argument is a user-defined class (or an instance thereof), - * uses of `getMethod` will find method definitions in that class (including inherited ones) rather than finding method calls. - * - * This example illustrates how this can be used to model cases where the library calls a specific named method on a user-defined class: - * ```rb - * class MyClass - * def doSomething - * x # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("doSomething").getReturn().asSink() - * end - * end - * Foo.bar MyClass.new - * ``` - * - * When modeling an external library that is known to call a specific method on a parameter (in this case `doSomething`), this makes - * it possible to find the corresponding method definition in user code. - * - * ### Strict left-to-right evaluation - * - * Most member predicates on this class are intended to be chained, and are always evaluated from left to right, which means - * the caller should restrict the initial set of values. - * - * For example, in the following snippet, we always find the uses of `Foo` before finding calls to `bar`: + * For example, a simplified way to get arguments to `Foo.bar` would be * ```ql - * API::getTopLevelMember("Foo").getMethod("bar") + * API::getTopLevelMember("Foo").getMethod("bar").getParameter(0).asSink() * ``` - * In particular, the implementation will never look for calls to `bar` and work backward from there. * - * Beware of the footgun that is to use API graphs with an unrestricted receiver: - * ```ql - * API::Node barCall(API::Node base) { - * result = base.getMethod("bar") // Do not do this! - * } + * The most commonly used accessors are `getMember`, `getMethod`, `getParameter`, and `getReturn`. + * + * ### API graph nodes + * + * There are two kinds of nodes in the API graphs, distinguished by who is "holding" the value: + * - **Use-nodes** represent values held by the current codebase, which came from an external library. + * (The current codebase is "using" a value that came from the library). + * - **Def-nodes** represent values held by the external library, which came from this codebase. + * (The current codebase "defines" the value seen by the library). + * + * API graph nodes are associated with data-flow nodes in the current codebase. + * (Since external libraries are not part of the database, there is no way to associate with concrete + * data-flow nodes from the external library). + * - **Use-nodes** are associated with data-flow nodes where a value enters the current codebase, + * such as the return value of a call to an external function. + * - **Def-nodes** are associated with data-flow nodes where a value leaves the current codebase, + * such as an argument passed in a call to an external function. + * + * + * ### Access paths and edge labels + * + * Nodes in the API graph are associated with a set of access paths, describing a series of operations + * that may be performed to obtain that value. + * + * For example, the access path `API::getTopLevelMember("Foo").getMethod("bar")` represents the action of + * reading the top-level constant `Foo` and then accessing the method `bar` on the resulting object. + * It would be associated with a call such as `Foo.bar()`. + * + * Each edge in the graph is labelled by such an "operation". For an edge `A->B`, the type of the `A` node + * determines who is performing the operation, and the type of the `B` node determines who ends up holding + * the result: + * - An edge starting from a use-node describes what the current codebase is doing to a value that + * came from a library. + * - An edge starting from a def-node describes what the external library might do to a value that + * came from the current codebase. + * - An edge ending in a use-node means the result ends up in the current codebase (at its associated data-flow node). + * - An edge ending in a def-node means the result ends up in external code (its associated data-flow node is + * the place where it was "last seen" in the current codebase before flowing out) + * + * Because the implementation of the external library is not visible, it is not known exactly what operations + * it will perform on values that flow there. Instead, the edges starting from a def-node are operations that would + * lead to an observable effect within the current codebase; without knowing for certain if the library will actually perform + * those operations. (When constructing these edges, we assume the library is somewhat well-behaved). + * + * For example, given this snippet: + * ```ruby + * Foo.bar(->(x) { doSomething(x) }) * ``` - * The above predicate does not restrict the receiver, and will thus perform an interprocedural data flow - * search starting at every node in the graph, which is very expensive. + * A callback is passed to the external function `Foo.bar`. We can't know if `Foo.bar` will actually invoke this callback. + * But _if_ the library should decide to invoke the callback, then a value will flow into the current codebase via the `x` parameter. + * For that reason, an edge is generated representing the argument-passing operation that might be performed by `Foo.bar`. + * This edge is going from the def-node associated with the callback to the use-node associated with the parameter `x` of the lambda. */ class Node extends Impl::TApiNode { /** - * Gets a data-flow node where this value may flow interprocedurally. + * Gets a data-flow node where this value may flow after entering the current codebase. * * This is similar to `asSource()` but additionally includes nodes that are transitively reachable by data flow. * See `asSource()` for examples. */ - bindingset[this] - pragma[inline_late] + pragma[inline] DataFlow::Node getAValueReachableFromSource() { result = getAValueReachableFromSourceInline(this) } @@ -174,14 +119,16 @@ module API { * end * ``` */ - bindingset[this] - pragma[inline_late] - DataFlow::LocalSourceNode asSource() { result = asSourceInline(this) } + pragma[inline] + DataFlow::LocalSourceNode asSource() { + result = pragma[only_bind_out](this).(Node::Internal).asSourceInternal() + } /** - * Gets a data-flow node where this value potentially flows into an external library. + * Gets a data-flow node where this value leaves the current codebase and flows into an + * external library (or in general, any external codebase). * - * This is usually the argument of a call, but can also be the return value of a callback. + * Concretely, this corresponds to an argument passed to a call to external code. * * For example: * ```ruby @@ -196,443 +143,15 @@ module API { * }) * ``` */ - bindingset[this] - pragma[inline_late] - DataFlow::Node asSink() { result = asSinkInline(this) } + DataFlow::Node asSink() { Impl::def(this, result) } /** - * Gets a callable that can reach this sink. - * - * For example: - * ```ruby - * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().asCallable() - * end - * - * class Baz - * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m").asCallable() - * end - * end - * Foo.bar(Baz.new) - * ``` - */ - bindingset[this] - pragma[inline_late] - DataFlow::CallableNode asCallable() { Impl::asCallable(this.getAnEpsilonSuccessor(), result) } - - /** - * Get a data-flow node that transitively flows to this value, provided that this value corresponds - * to a sink. + * Get a data-flow node that transitively flows to an external library (or in general, any external codebase). * * This is similar to `asSink()` but additionally includes nodes that transitively reach a sink by data flow. * See `asSink()` for examples. */ - bindingset[this] - pragma[inline_late] - DataFlow::Node getAValueReachingSink() { result = getAValueReachingSinkInline(this) } - - /** - * Gets a module or class referred to by this API node. - * - * For example: - * ```ruby - * module Foo - * class Bar # API::getTopLevelMember("Foo").getMember("Bar").asModule() - * end - * end - * ``` - */ - bindingset[this] - pragma[inline_late] - DataFlow::ModuleNode asModule() { this = Impl::MkModuleObjectDown(result) } - - /** - * Gets the call referred to by this API node. - * - * For example: - * ```ruby - * # API::getTopLevelMember("Foo").getMethod("bar").asCall() - * Foo.bar - * - * class Bar < Foo - * def doSomething - * # API::getTopLevelMember("Foo").getInstance().getMethod("baz").asCall() - * baz - * end - * end - * ``` - */ - bindingset[this] - pragma[inline_late] - DataFlow::CallNode asCall() { this = Impl::MkMethodAccessNode(result) } - - /** - * DEPRECATED. Use `asCall()` instead. - */ - pragma[inline] - deprecated DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } - - /** - * Gets a module or class that descends from the module or class referenced by this API node. - */ - bindingset[this] - pragma[inline_late] - DataFlow::ModuleNode getADescendentModule() { result = this.getAnEpsilonSuccessor().asModule() } - - /** - * Gets a call to a method on the receiver represented by this API node. - * - * This is a shorthand for `getMethod(method).asCall()`, and thus returns a data-flow node - * rather than an API node. - * - * For example: - * ```ruby - * # API::getTopLevelMember("Foo").getAMethodCall("bar") - * Foo.bar - * ``` - */ - pragma[inline] - DataFlow::CallNode getAMethodCall(string method) { - // This predicate is currently not 'inline_late' because 'method' can be an input or output - result = this.getMethod(method).asCall() - } - - /** - * Gets an access to the constant `m` with this value as the base of the access. - * - * For example: - * ```ruby - * A::B # API::getATopLevelMember("A").getMember("B") - * - * module A - * class B # API::getATopLevelMember("A").getMember("B") - * end - * end - * ``` - */ - pragma[inline] - Node getMember(string m) { - // This predicate is currently not 'inline_late' because 'm' can be an input or output - Impl::memberEdge(this.getAnEpsilonSuccessor(), m, result) - } - - /** - * Gets an access to a constant with this value as the base of the access. - * - * This is equivalent to `getMember(_)` but can be more efficient. - */ - bindingset[this] - pragma[inline_late] - Node getAMember() { Impl::anyMemberEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets a node that may refer to an instance of the module or class represented by this API node. - * - * This includes the following: - * - Calls to `new` on this module or class or a descendent thereof - * - References to `self` in instance methods declared in any ancestor of any descendent of this module or class - * - * For example: - * ```ruby - * A.new # API::getTopLevelMember("A").getInstance() - * - * class B < A - * def m - * self # API::getTopLevelMember("A").getInstance() - * end - * end - * - * B.new # API::getTopLevelMember("A").getInstance() - * - * class C < A - * include Mixin - * end - * module Mixin - * def m - * # Although 'Mixin' is not directly related to 'A', 'self' may refer to an instance of 'A' - * # due to its inclusion in a subclass of 'A'. - * self # API::getTopLevelMember("A").getInstance() - * end - * end - * ``` - */ - bindingset[this] - pragma[inline_late] - Node getInstance() { Impl::instanceEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets a call to `method` with this value as the receiver, or the definition of `method` on - * an object that can reach this sink. - * - * If the receiver represents a module or class object, this includes calls on descendents of that module or class. - * - * For example: - * ```ruby - * # API::getTopLevelMember("Foo").getMethod("bar") - * Foo.bar - * - * # API::getTopLevelMember("Foo").getInstance().getMethod("bar") - * Foo.new.bar - * - * class B < Foo - * end - * B.bar # API::getTopLevelMember("Foo").getMethod("bar") - * - * class C - * def m # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getMethod("m") - * end - * end - * Foo.bar(C.new) - * ``` - */ - pragma[inline] - Node getMethod(string method) { - // TODO: Consider 'getMethodTarget(method)' for looking up method definitions? - // This predicate is currently not 'inline_late' because 'method' can be an input or output - Impl::methodEdge(this.getAnEpsilonSuccessor(), method, result) - } - - /** - * Gets the result of this call, or the return value of this callable. - */ - bindingset[this] - pragma[inline_late] - Node getReturn() { Impl::returnEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets the result of a call to `method` with this value as the receiver, or the return value of `method` defined on - * an object that can reach this sink. - * - * This is a shorthand for `getMethod(method).getReturn()`. - */ - pragma[inline] - Node getReturn(string method) { - // This predicate is currently not 'inline_late' because 'method' can be an input or output - result = this.getMethod(method).getReturn() - } - - /** - * Gets the `n`th positional argument to this call. - * - * For example, this would get `x` in the following snippet: - * ```ruby - * Foo.bar(x) # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0) - * ``` - */ - pragma[inline] - Node getArgument(int n) { - // This predicate is currently not 'inline_late' because 'n' can be an input or output - Impl::positionalArgumentEdge(this, n, result) - } - - /** - * Gets the given keyword argument to this call. - * - * For example, this would get `x` in the following snippet: - * ```ruby - * Foo.bar(baz: x) # API::getTopLevelMember("Foo").getMethod("bar").getKeywordArgument("baz") - * ``` - */ - pragma[inline] - Node getKeywordArgument(string name) { - // This predicate is currently not 'inline_late' because 'name' can be an input or output - Impl::keywordArgumentEdge(this, name, result) - } - - /** - * Gets the block parameter of a callable that can reach this sink. - * - * For example, this would get the `&blk` in the following snippet: - * ```ruby - * # API::getTopLevelMember("Foo").getMethod("bar").getArgument(0).getBlockParameter() - * Foo.bar(->(&blk) {}) - * end - * ``` - */ - bindingset[this] - pragma[inline_late] - Node getBlockParameter() { Impl::blockParameterEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets the `n`th positional parameter of this callable, or the `n`th positional argument to this call. - * - * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. - * When referring to an argument, it is recommended to use `getArgument(n)` instead. - */ - pragma[inline] - Node getParameter(int n) { - // This predicate is currently not 'inline_late' because 'n' can be an input or output - Impl::positionalParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), n, result) - } - - /** - * Gets the given keyword parameter of this callable, or keyword argument to this call. - * - * Note: for historical reasons, this predicate may refer to an argument of a call, but this may change in the future. - * When referring to an argument, it is recommended to use `getKeywordArgument(n)` instead. - */ - pragma[inline] - Node getKeywordParameter(string name) { - // This predicate is currently not 'inline_late' because 'name' can be an input or output - Impl::keywordParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), name, result) - } - - /** - * Gets the block argument to this call, or the block parameter of this callable. - * - * Note: this predicate may refer to either an argument or a parameter. When referring to a block parameter, - * it is recommended to use `getBlockParameter()` instead. - * - * For example: - * ```ruby - * Foo.bar do |x| # API::getTopLevelMember("Foo").getMethod("bar").getBlock().getParameter(0) - * end - * ``` - */ - bindingset[this] - pragma[inline_late] - Node getBlock() { Impl::blockParameterOrArgumentEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets the argument passed in argument position `pos` at this call. - */ - pragma[inline] - Node getArgumentAtPosition(DataFlowDispatch::ArgumentPosition pos) { - // This predicate is currently not 'inline_late' because 'pos' can be an input or output - Impl::argumentEdge(pragma[only_bind_out](this), pos, result) // note: no need for epsilon step since 'this' must be a call - } - - /** - * Gets the parameter at position `pos` of this callable. - */ - pragma[inline] - Node getParameterAtPosition(DataFlowDispatch::ParameterPosition pos) { - // This predicate is currently not 'inline_late' because 'pos' can be an input or output - Impl::parameterEdge(this.getAnEpsilonSuccessor(), pos, result) - } - - /** - * Gets a `new` call with this value as the receiver. - */ - bindingset[this] - pragma[inline_late] - DataFlow::ExprNode getAnInstantiation() { result = this.getReturn("new").asSource() } - - /** - * Gets a representative for the `content` of this value. - * - * When possible, it is preferrable to use one of the specialized variants of this predicate, such as `getAnElement`. - * - * Concretely, this gets sources where `content` is read from this value, and as well as sinks where - * `content` is stored onto this value or onto an object that can reach this sink. - */ - pragma[inline] - Node getContent(DataFlow::Content content) { - // This predicate is currently not 'inline_late' because 'content' can be an input or output - Impl::contentEdge(this.getAnEpsilonSuccessor(), content, result) - } - - /** - * Gets a representative for the `contents` of this value. - * - * See `getContent()` for more details. - */ - bindingset[this, contents] - pragma[inline_late] - Node getContents(DataFlow::ContentSet contents) { - // We always use getAStoreContent when generating content edges, and we always use getAReadContent when querying the graph. - result = this.getContent(contents.getAReadContent()) - } - - /** - * Gets a representative for the instance field of the given `name`, which must include the `@` character. - * - * This can be used to find cases where a class accesses the fields used by a base class. - * - * ```ruby - * class A < B - * def m - * @foo # API::getTopLevelMember("B").getInstance().getField("@foo") - * end - * end - * ``` - */ - pragma[inline] - Node getField(string name) { - // This predicate is currently not 'inline_late' because 'name' can be an input or output - Impl::fieldEdge(this.getAnEpsilonSuccessor(), name, result) - } - - /** - * Gets a representative for an arbitrary element of this collection. - * - * For example: - * ```ruby - * Foo.bar.each do |x| # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() - * end - * - * Foo.bar[0] # API::getTopLevelMember("Foo").getMethod("bar").getReturn().getAnElement() - * ``` - */ - bindingset[this] - pragma[inline_late] - Node getAnElement() { Impl::elementEdge(this.getAnEpsilonSuccessor(), result) } - - /** - * Gets the data-flow node that gives rise to this node, if any. - */ - DataFlow::Node getInducingNode() { - this = Impl::MkMethodAccessNode(result) or - this = Impl::MkBackwardNode(result, _) or - this = Impl::MkForwardNode(result, _) or - this = Impl::MkSinkNode(result) - } - - /** Gets the location of this node. */ - Location getLocation() { - result = this.getInducingNode().getLocation() - or - exists(DataFlow::ModuleNode mod | - this = Impl::MkModuleObjectDown(mod) - or - this = Impl::MkModuleInstanceUp(mod) - | - result = mod.getLocation() - ) - or - this instanceof RootNode and - result instanceof EmptyLocation - } - - /** - * Gets a textual representation of this element. - */ - string toString() { none() } - - /** - * Gets a node representing a (direct or indirect) subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. - */ - pragma[inline] - deprecated Node getASubclass() { result = this } - - /** - * Gets a node representing a direct subclass of the class represented by this node. - * ```rb - * class A; end - * class B < A; end - * class C < B; end - * ``` - * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. - */ - pragma[inline] - deprecated Node getAnImmediateSubclass() { - result = this.asModule().getAnImmediateDescendent().trackModule() - } + DataFlow::Node getAValueReachingSink() { result = Impl::trackDefNode(this.asSink()) } /** DEPRECATED. This predicate has been renamed to `getAValueReachableFromSource()`. */ deprecated DataFlow::Node getAUse() { result = this.getAValueReachableFromSource() } @@ -647,143 +166,326 @@ module API { deprecated DataFlow::Node getAValueReachingRhs() { result = this.getAValueReachingSink() } /** - * DEPRECATED. API graph nodes are no longer associated with specific paths. - * - * Gets a string representation of the lexicographically least among all shortest access paths - * from the root to this node. - */ - deprecated string getPath() { none() } - - /** - * DEPRECATED. Use label-specific predicates in this class, such as `getMember`, instead of using `getASuccessor`. - * - * Gets a node such that there is an edge in the API graph between this node and the other - * one, and that edge is labeled with `lbl`. + * Gets a call to a method on the receiver represented by this API component. */ pragma[inline] - deprecated Node getASuccessor(Label::ApiLabel lbl) { - labelledEdge(this.getAnEpsilonSuccessor(), lbl, result) + DataFlow::CallNode getAMethodCall(string method) { result = this.getReturn(method).asSource() } + + /** + * Gets a node representing member `m` of this API component. + * + * For example, a member can be: + * + * - A submodule of a module + * - An attribute of an object + */ + pragma[inline] + Node getMember(string m) { + result = pragma[only_bind_out](this).(Node::Internal).getMemberInternal(m) } /** - * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get - * a node intended for backtracking. + * Gets a node representing a member of this API component where the name of the member may + * or may not be known statically. + */ + cached + Node getAMember() { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::member(_)) + } + + /** + * Gets a node representing an instance of this API component, that is, an object whose + * constructor is the function represented by this node. * + * For example, if this node represents a use of some class `A`, then there might be a node + * representing instances of `A`, typically corresponding to expressions `A.new` at the + * source level. + * + * This predicate may have multiple results when there are multiple constructor calls invoking this API component. + * Consider using `getAnInstantiation()` if there is a need to distinguish between individual constructor calls. + */ + pragma[inline] + Node getInstance() { result = this.getASubclass().getReturn("new") } + + /** + * Gets a node representing a call to `method` on the receiver represented by this node. + */ + pragma[inline] + MethodAccessNode getMethod(string method) { + result = pragma[only_bind_out](this).(Node::Internal).getMethodInternal(method) + } + + /** + * Gets a node representing the result of this call. + */ + pragma[inline] + Node getReturn() { result = pragma[only_bind_out](this).(Node::Internal).getReturnInternal() } + + /** + * Gets a node representing the result of calling a method on the receiver represented by this node. + */ + pragma[inline] + Node getReturn(string method) { result = this.getMethod(method).getReturn() } + + /** Gets an API node representing the `n`th positional parameter. */ + cached + Node getParameter(int n) { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::parameter(n)) + } + + /** Gets an API node representing the given keyword parameter. */ + cached + Node getKeywordParameter(string name) { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::keywordParameter(name)) + } + + /** Gets an API node representing the block parameter. */ + cached + Node getBlock() { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::blockParameter()) + } + + /** + * Gets a `new` call to the function represented by this API component. + */ + pragma[inline] + DataFlow::ExprNode getAnInstantiation() { result = this.getInstance().asSource() } + + /** + * Gets a node representing a (direct or indirect) subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getASubclass()` will return uses of `A`, `B` and `C`. + */ + Node getASubclass() { result = this.getAnImmediateSubclass*() } + + /** + * Gets a node representing a direct subclass of the class represented by this node. + * ```rb + * class A; end + * class B < A; end + * class C < B; end + * ``` + * In the example above, `getMember("A").getAnImmediateSubclass()` will return uses of `B` only. + */ + cached + Node getAnImmediateSubclass() { + Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::subclass()) + } + + /** + * Gets a node representing the `content` stored on the base object. + */ + cached + Node getContent(DataFlow::Content content) { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::content(content)) + } + + /** + * Gets a node representing the `contents` stored on the base object. + */ + pragma[inline] + Node getContents(DataFlow::ContentSet contents) { + // We always use getAStoreContent when generating the graph, and we always use getAReadContent when querying the graph. + result = this.getContent(contents.getAReadContent()) + } + + /** Gets a node representing the instance field of the given `name`, which must include the `@` character. */ + cached + Node getField(string name) { + Impl::forceCachingInSameStage() and + result = this.getContent(DataFlowPrivate::TFieldContent(name)) + } + + /** Gets a node representing an element of this collection (known or unknown). */ + cached + Node getAnElement() { + Impl::forceCachingInSameStage() and + result = this.getContents(any(DataFlow::ContentSet set | set.isAnyElement())) + } + + /** + * Gets a string representation of the lexicographically least among all shortest access paths + * from the root to this node. + */ + string getPath() { + result = min(string p | p = this.getAPath(Impl::distanceFromRoot(this)) | p) + } + + /** + * Gets a node such that there is an edge in the API graph between this node and the other + * one, and that edge is labeled with `lbl`. + */ + Node getASuccessor(Label::ApiLabel lbl) { Impl::edge(this, lbl, result) } + + /** * Gets a node such that there is an edge in the API graph between that other node and * this one, and that edge is labeled with `lbl` */ - deprecated Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } + Node getAPredecessor(Label::ApiLabel lbl) { this = result.getASuccessor(lbl) } /** - * DEPRECATED. API graphs no longer support backward traversal of edges. If possible use `.backtrack()` to get - * a node intended for backtracking. - * * Gets a node such that there is an edge in the API graph between this node and the other * one. */ - deprecated Node getAPredecessor() { result = this.getAPredecessor(_) } + Node getAPredecessor() { result = this.getAPredecessor(_) } /** * Gets a node such that there is an edge in the API graph between that other node and * this one. */ - pragma[inline] - deprecated Node getASuccessor() { result = this.getASuccessor(_) } + Node getASuccessor() { result = this.getASuccessor(_) } - /** DEPRECATED. API graphs are no longer associated with a depth. */ - deprecated int getDepth() { none() } + /** + * Gets the data-flow node that gives rise to this node, if any. + */ + DataFlow::Node getInducingNode() { + this = Impl::MkUse(result) + or + this = Impl::MkDef(result) + or + this = Impl::MkMethodAccessNode(result) + } - pragma[inline] - private Node getAnEpsilonSuccessor() { result = getAnEpsilonSuccessorInline(this) } + /** Gets the location of this node. */ + Location getLocation() { + result = this.getInducingNode().getLocation() + or + exists(DataFlow::ModuleNode mod | + this = Impl::MkModuleObject(mod) and + result = mod.getLocation() + ) + or + // For nodes that do not have a meaningful location, `path` is the empty string and all other + // parameters are zero. + not exists(this.getInducingNode()) and + result instanceof EmptyLocation + } + + /** + * Gets a textual representation of this element. + */ + string toString() { none() } + + /** + * Gets a path of the given `length` from the root to this node. + */ + private string getAPath(int length) { + this instanceof Impl::MkRoot and + length = 0 and + result = "" + or + exists(Node pred, Label::ApiLabel lbl, string predpath | + Impl::edge(pred, lbl, this) and + predpath = pred.getAPath(length - 1) and + exists(string dot | if length = 1 then dot = "" else dot = "." | + result = predpath + dot + lbl and + // avoid producing strings longer than 1MB + result.length() < 1000 * 1000 + ) + ) and + length in [1 .. Impl::distanceFromRoot(this)] + } + + /** Gets the shortest distance from the root to this node in the API graph. */ + int getDepth() { result = Impl::distanceFromRoot(this) } } - /** DEPRECATED. Use `API::root()` to access the root node. */ - deprecated class Root = RootNode; + /** Companion module to the `Node` class. */ + module Node { + /** + * INTERNAL USE ONLY. + * + * An API node, with some internal predicates exposed. + */ + class Internal extends Node { + /** + * INTERNAL USE ONLY. + * + * Same as `asSource()` but without join-order hints. + */ + cached + DataFlow::LocalSourceNode asSourceInternal() { + Impl::forceCachingInSameStage() and + Impl::use(this, result) + } - /** DEPRECATED. A node corresponding to the use of an API component. */ - deprecated class Use = ForwardNode; + /** + * Same as `getMember` but without join-order hints. + */ + cached + Node getMemberInternal(string m) { + Impl::forceCachingInSameStage() and + result = this.getASuccessor(Label::member(m)) + } - /** DEPRECATED. A node corresponding to a value escaping into an API component. */ - deprecated class Def = SinkNode; + /** + * Same as `getMethod` but without join-order hints. + */ + cached + MethodAccessNode getMethodInternal(string method) { + Impl::forceCachingInSameStage() and + result = this.getASubclass().getASuccessor(Label::method(method)) + } + + /** + * INTERNAL USE ONLY. + * + * Same as `getReturn()` but without join-order hints. + */ + cached + Node getReturnInternal() { + Impl::forceCachingInSameStage() and result = this.getASuccessor(Label::return()) + } + } + } + + bindingset[node] + pragma[inline_late] + private DataFlow::Node getAValueReachableFromSourceInline(Node node) { + exists(DataFlow::LocalSourceNode src, DataFlow::LocalSourceNode dst | + Impl::use(node, pragma[only_bind_into](src)) and + pragma[only_bind_into](dst) = Impl::trackUseNode(src) and + dst.flowsTo(result) + ) + } /** The root node of an API graph. */ - private class RootNode extends Node, Impl::MkRoot { - override string toString() { result = "Root()" } + class Root extends Node, Impl::MkRoot { + override string toString() { result = "root" } } - /** A node representing a given type-tracking state when tracking forwards. */ - private class ForwardNode extends Node, Impl::MkForwardNode { - private DataFlow::LocalSourceNode node; - private TypeTracker tracker; - - ForwardNode() { this = Impl::MkForwardNode(node, tracker) } - - override string toString() { - if tracker.start() - then result = "ForwardNode(" + node + ")" - else result = "ForwardNode(" + node + ", " + tracker + ")" - } + private string tryGetPath(Node node) { + result = node.getPath() + or + not exists(node.getPath()) and + result = "with no path" } - /** A node representing a given type-tracking state when tracking backwards. */ - private class BackwardNode extends Node, Impl::MkBackwardNode { - private DataFlow::LocalSourceNode node; - private TypeTracker tracker; - - BackwardNode() { this = Impl::MkBackwardNode(node, tracker) } - - override string toString() { - if tracker.start() - then result = "BackwardNode(" + node + ")" - else result = "BackwardNode(" + node + ", " + tracker + ")" - } + /** A node corresponding to the use of an API component. */ + class Use extends Node, Impl::MkUse { + override string toString() { result = "Use " + tryGetPath(this) } } - /** A node representing a module/class object with epsilon edges to its descendents. */ - private class ModuleObjectDownNode extends Node, Impl::MkModuleObjectDown { - /** Gets the module represented by this API node. */ - DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectDown(result) } - - override string toString() { result = "ModuleObjectDown(" + this.getModule() + ")" } - } - - /** A node representing a module/class object with epsilon edges to its ancestors. */ - private class ModuleObjectUpNode extends Node, Impl::MkModuleObjectUp { - /** Gets the module represented by this API node. */ - DataFlow::ModuleNode getModule() { this = Impl::MkModuleObjectUp(result) } - - override string toString() { result = "ModuleObjectUp(" + this.getModule() + ")" } - } - - /** A node representing instances of a module/class with epsilon edges to its ancestors. */ - private class ModuleInstanceUpNode extends Node, Impl::MkModuleInstanceUp { - /** Gets the module whose instances are represented by this API node. */ - DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceUp(result) } - - override string toString() { result = "ModuleInstanceUp(" + this.getModule() + ")" } - } - - /** A node representing instances of a module/class with epsilon edges to its descendents. */ - private class ModuleInstanceDownNode extends Node, Impl::MkModuleInstanceDown { - /** Gets the module whose instances are represented by this API node. */ - DataFlow::ModuleNode getModule() { this = Impl::MkModuleInstanceDown(result) } - - override string toString() { result = "ModuleInstanceDown(" + this.getModule() + ")" } + /** A node corresponding to a value escaping into an API component. */ + class Def extends Node, Impl::MkDef { + override string toString() { result = "Def " + tryGetPath(this) } } /** A node corresponding to the method being invoked at a method call. */ class MethodAccessNode extends Node, Impl::MkMethodAccessNode { - override string toString() { result = "MethodAccessNode(" + this.asCall() + ")" } - } + override string toString() { result = "MethodAccessNode " + tryGetPath(this) } - /** - * A node corresponding to an argument, right-hand side of a store, or return value from a callable. - * - * Such a node may serve as the starting-point of backtracking, and has epsilon edges going to - * the backward nodes corresponding to `getALocalSource`. - */ - private class SinkNode extends Node, Impl::MkSinkNode { - override string toString() { result = "SinkNode(" + this.getInducingNode() + ")" } + /** Gets the call node corresponding to this method access. */ + DataFlow::CallNode getCallNode() { this = Impl::MkMethodAccessNode(result) } } /** @@ -797,8 +499,6 @@ module API { * additional entry points may be added by extending this class. */ abstract class EntryPoint extends string { - // Note: this class can be deprecated in Ruby, but is still referenced by shared code in ApiGraphModels.qll, - // where it can't be removed since other languages are still dependent on the EntryPoint class. bindingset[this] EntryPoint() { any() } @@ -818,7 +518,7 @@ module API { DataFlow::CallNode getACall() { none() } /** Gets an API-node for this entry point. */ - API::Node getANode() { Impl::entryPointEdge(this, result) } + API::Node getANode() { result = root().getASuccessor(Label::entryPoint(this)) } } // Ensure all entry points are imported from ApiGraphs.qll @@ -827,301 +527,88 @@ module API { } /** Gets the root node. */ - Node root() { result instanceof RootNode } + Root root() { any() } /** - * Gets an access to the top-level constant `name`. + * Gets a node corresponding to a top-level member `m` (typically a module). * - * To access nested constants, use `getMember()` on the resulting node. For example, for nodes corresponding to the class `Gem::Version`, + * This is equivalent to `root().getAMember("m")`. + * + * Note: You should only use this predicate for top level modules or classes. If you want nodes corresponding to a nested module or class, + * you should use `.getMember` on the parent module/class. For example, for nodes corresponding to the class `Gem::Version`, * use `getTopLevelMember("Gem").getMember("Version")`. */ - pragma[inline] - Node getTopLevelMember(string name) { Impl::topLevelMember(name, result) } + cached + Node getTopLevelMember(string m) { + Impl::forceCachingInSameStage() and result = root().(Node::Internal).getMemberInternal(m) + } /** - * Gets an unqualified call at the top-level with the given method name. + * Provides the actual implementation of API graphs, cached for performance. + * + * Ideally, we'd like nodes to correspond to (global) access paths, with edge labels + * corresponding to extending the access path by one element. We also want to be able to map + * nodes to their definitions and uses in the data-flow graph, and this should happen modulo + * (inter-procedural) data flow. + * + * This, however, is not easy to implement, since access paths can have unbounded length + * and we need some way of recognizing cycles to avoid non-termination. Unfortunately, expressing + * a condition like "this node hasn't been involved in constructing any predecessor of + * this node in the API graph" without negative recursion is tricky. + * + * So instead most nodes are directly associated with a data-flow node, representing + * either a use or a definition of an API component. This ensures that we only have a finite + * number of nodes. However, we can now have multiple nodes with the same access + * path, which are essentially indistinguishable for a client of the API. + * + * On the other hand, a single node can have multiple access paths (which is, of + * course, unavoidable). We pick as canonical the alphabetically least access path with + * shortest length. */ - pragma[inline] - MethodAccessNode getTopLevelCall(string name) { Impl::toplevelCall(name, result) } - - pragma[nomagic] - private predicate isReachable(DataFlow::LocalSourceNode node, TypeTracker t) { - t.start() and exists(node) - or - exists(DataFlow::LocalSourceNode prev, TypeTracker t2 | - isReachable(prev, t2) and - node = prev.track(t2, t) and - notSelfParameter(node) - ) - } - - bindingset[node] - pragma[inline_late] - private predicate notSelfParameter(DataFlow::Node node) { - not node instanceof DataFlow::SelfParameterNode - } - - private module SharedArg implements ApiGraphSharedSig { - class ApiNode = Node; - - ApiNode getForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { - result = Impl::MkForwardNode(node, t) - } - - ApiNode getBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { - result = Impl::MkBackwardNode(node, t) - } - - ApiNode getSinkNode(DataFlow::Node node) { result = Impl::MkSinkNode(node) } - - pragma[nomagic] - predicate specificEpsilonEdge(ApiNode pred, ApiNode succ) { - exists(DataFlow::ModuleNode mod | - moduleReferenceEdge(mod, pred, succ) - or - moduleInheritanceEdge(mod, pred, succ) - or - pred = getForwardEndNode(getSuperClassNode(mod)) and - succ = Impl::MkModuleObjectDown(mod) - ) - or - implicitCallEdge(pred, succ) - or - exists(DataFlow::HashLiteralNode splat | hashSplatEdge(splat, pred, succ)) - } - - /** - * Holds if the epsilon edge `pred -> succ` should be generated, to handle inheritance relations of `mod`. - */ - pragma[inline] - private predicate moduleInheritanceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { - pred = Impl::MkModuleObjectDown(mod) and - succ = Impl::MkModuleObjectDown(mod.getAnImmediateDescendent()) - or - pred = Impl::MkModuleInstanceDown(mod) and - succ = Impl::MkModuleInstanceDown(mod.getAnImmediateDescendent()) - or - exists(DataFlow::ModuleNode ancestor | - ancestor = mod.getAnImmediateAncestor() and - // Restrict flow back to Object to avoid spurious flow for methods that happen - // to exist on Object, such as top-level methods. - not ancestor.getQualifiedName() = "Object" - | - pred = Impl::MkModuleInstanceUp(mod) and - succ = Impl::MkModuleInstanceUp(ancestor) - or - pred = Impl::MkModuleObjectUp(mod) and - succ = Impl::MkModuleObjectUp(ancestor) - ) - or - // Due to multiple inheritance, allow upwards traversal after downward traversal, - // so we can detect calls sideways in the hierarchy. - // Note that a similar case does not exist for ModuleObject since singleton methods are only inherited - // from the superclass, and there can only be one superclass. - pred = Impl::MkModuleInstanceDown(mod) and - succ = Impl::MkModuleInstanceUp(mod) - } - - /** - * Holds if the epsilon `pred -> succ` should be generated, to associate `mod` with its references in the codebase. - */ - bindingset[mod] - pragma[inline_late] - private predicate moduleReferenceEdge(DataFlow::ModuleNode mod, ApiNode pred, ApiNode succ) { - pred = Impl::MkModuleObjectDown(mod) and - succ = getForwardStartNode(getAModuleReference(mod)) - or - pred = getBackwardEndNode(getAModuleReference(mod)) and - ( - succ = Impl::MkModuleObjectUp(mod) - or - succ = Impl::MkModuleObjectDown(mod) - ) - or - pred = Impl::MkModuleInstanceUp(mod) and - succ = getAModuleInstanceUseNode(mod) - or - pred = getAModuleInstanceDefNode(mod) and - succ = Impl::MkModuleInstanceUp(mod) - or - pred = getAModuleDescendentInstanceDefNode(mod) and - succ = Impl::MkModuleInstanceDown(mod) - } - - /** - * Holds if the epsilon step `pred -> succ` should be generated to account for the fact that `getMethod("call")` - * may be omitted when dealing with blocks, lambda, or procs. - * - * For example, a block may be invoked by a `yield`, or can be converted to a proc and then invoked via `.call`. - * To simplify this, the implicit proc conversion is seen as a no-op and the `.call` is omitted. - */ - pragma[nomagic] - private predicate implicitCallEdge(ApiNode pred, ApiNode succ) { - // Step from &block parameter to yield call without needing `getMethod("call")`. - exists(DataFlow::MethodNode method | - pred = getForwardEndNode(method.getBlockParameter()) and - succ = Impl::MkMethodAccessNode(method.getABlockCall()) - ) - or - // Step from x -> x.call (the call itself, not its return value), without needing `getMethod("call")`. - exists(DataFlow::CallNode call | - call.getMethodName() = "call" and - pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and - succ = Impl::MkMethodAccessNode(call) - ) - or - exists(DataFlow::ModuleNode mod | - // Step from module/class to its own `call` method without needing `getMethod("call")`. - (pred = Impl::MkModuleObjectDown(mod) or pred = Impl::MkModuleObjectUp(mod)) and - succ = getBackwardEndNode(mod.getOwnSingletonMethod("call")) - or - pred = Impl::MkModuleInstanceUp(mod) and - succ = getBackwardEndNode(mod.getOwnInstanceMethod("call")) - ) - } - - pragma[nomagic] - private DataFlow::Node getHashSplatArgument(DataFlow::HashLiteralNode literal) { - result = DataFlowPrivate::TSynthHashSplatArgumentNode(literal.asExpr()) - } - - /** - * Holds if the epsilon edge `pred -> succ` should be generated to account for the members of a hash literal. - * - * This currently exists because hash literals are desugared to `Hash.[]` calls, whose summary relies on `WithContent`. - * However, `contentEdge` does not currently generate edges for `WithContent` steps. - */ - bindingset[literal] - pragma[inline_late] - private predicate hashSplatEdge(DataFlow::HashLiteralNode literal, ApiNode pred, ApiNode succ) { - exists(TypeTracker t | - pred = Impl::MkForwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and - succ = Impl::MkForwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) - or - succ = Impl::MkBackwardNode(getALocalSourceStrict(getHashSplatArgument(literal)), t) and - pred = Impl::MkBackwardNode(pragma[only_bind_out](literal), pragma[only_bind_out](t)) - ) - } - - pragma[nomagic] - private DataFlow::LocalSourceNode getAModuleReference(DataFlow::ModuleNode mod) { - result = mod.getAnImmediateReference() - or - mod.getAnAncestor().getAnOwnInstanceSelf() = getANodeReachingClassCall(result) - } - - /** - * Gets an API node that may refer to an instance of `mod`. - */ - bindingset[mod] - pragma[inline_late] - private ApiNode getAModuleInstanceUseNode(DataFlow::ModuleNode mod) { - result = getForwardStartNode(mod.getAnOwnInstanceSelf()) - } - - /** - * Gets a node that can be backtracked to an instance of `mod`. - */ - bindingset[mod] - pragma[inline_late] - private ApiNode getAModuleInstanceDefNode(DataFlow::ModuleNode mod) { - result = getBackwardEndNode(mod.getAnImmediateReference().getAMethodCall("new")) - } - - /** - * Gets a node that can be backtracked to an instance of `mod` or any of its descendents. - */ - bindingset[mod] - pragma[inline_late] - private ApiNode getAModuleDescendentInstanceDefNode(DataFlow::ModuleNode mod) { - result = getBackwardEndNode(mod.getAnOwnInstanceSelf()) - } - - /** - * Holds if `superclass` is the superclass of `mod`. - */ - pragma[nomagic] - private DataFlow::LocalSourceNode getSuperClassNode(DataFlow::ModuleNode mod) { - result.getALocalUse().asExpr().getExpr() = - mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() - } - - /** Gets a node that can reach the receiver of the given `.class` call. */ - private DataFlow::LocalSourceNode getANodeReachingClassCall( - DataFlow::CallNode call, TypeBackTracker t - ) { - t.start() and - call.getMethodName() = "class" and - result = getALocalSourceStrict(call.getReceiver()) - or - exists(DataFlow::LocalSourceNode prev, TypeBackTracker t2 | - prev = getANodeReachingClassCall(call, t2) and - result = prev.backtrack(t2, t) and - notSelfParameter(prev) - ) - } - - /** Gets a node that can reach the receiver of the given `.class` call. */ - private DataFlow::LocalSourceNode getANodeReachingClassCall(DataFlow::CallNode call) { - result = getANodeReachingClassCall(call, TypeBackTracker::end()) - } - } - - /** INTERNAL USE ONLY. */ - module Internal { - private module Shared = ApiGraphShared; - - import Shared - - /** Gets the API node corresponding to the module/class object for `mod`. */ - bindingset[mod] - pragma[inline_late] - Node getModuleNode(DataFlow::ModuleNode mod) { result = Impl::MkModuleObjectDown(mod) } - - /** Gets the API node corresponding to instances of `mod`. */ - bindingset[mod] - pragma[inline_late] - Node getModuleInstance(DataFlow::ModuleNode mod) { result = getModuleNode(mod).getInstance() } - } - - private import Internal - import Internal::Public - cached private module Impl { + cached + predicate forceCachingInSameStage() { any() } + + cached + predicate forceCachingBackref() { + 1 = 1 + or + exists(getTopLevelMember(_)) + or + exists( + any(Node n) + .(Node::Internal) + .getMemberInternal("foo") + .getAMember() + .(Node::Internal) + .getMethodInternal("foo") + .(Node::Internal) + .getReturnInternal() + .getParameter(0) + .getKeywordParameter("foo") + .getBlock() + .getAnImmediateSubclass() + .getContent(_) + .getField(_) + .getAnElement() + .(Node::Internal) + .asSourceInternal() + ) + } + cached newtype TApiNode = /** The root of the API graph. */ MkRoot() or /** The method accessed at `call`, synthetically treated as a separate object. */ - MkMethodAccessNode(DataFlow::CallNode call) or - /** The module object `mod` with epsilon edges to its ancestors. */ - MkModuleObjectUp(DataFlow::ModuleNode mod) or - /** The module object `mod` with epsilon edges to its descendents. */ - MkModuleObjectDown(DataFlow::ModuleNode mod) or - /** Instances of `mod` with epsilon edges to its ancestors. */ - MkModuleInstanceUp(DataFlow::ModuleNode mod) or - /** Instances of `mod` with epsilon edges to its descendents, and to its upward node. */ - MkModuleInstanceDown(DataFlow::ModuleNode mod) or - /** Intermediate node for following forward data flow. */ - MkForwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or - /** Intermediate node for following backward data flow. */ - MkBackwardNode(DataFlow::LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or - MkSinkNode(DataFlow::Node node) { needsSinkNode(node) } - - private predicate needsSinkNode(DataFlow::Node node) { - node instanceof DataFlowPrivate::ArgumentNode - or - TypeTrackerSpecific::basicStoreStep(node, _, _) - or - node = any(DataFlow::CallableNode callable).getAReturnNode() - or - node = any(EntryPoint e).getASink() - } - - bindingset[e] - pragma[inline_late] - private DataFlow::Node getNodeFromExpr(Expr e) { result.asExpr().getExpr() = e } + MkMethodAccessNode(DataFlow::CallNode call) { isUse(call) } or + /** A use of an API member at the node `nd`. */ + MkUse(DataFlow::Node nd) { isUse(nd) } or + /** A value that escapes into an external library at the node `nd` */ + MkDef(DataFlow::Node nd) { isDef(nd) } or + /** A module object seen as a use node. */ + MkModuleObject(DataFlow::ModuleNode mod) private string resolveTopLevel(ConstantReadAccess read) { result = read.getModule().getQualifiedName() and @@ -1129,288 +616,300 @@ module API { } /** - * Holds `pred` should have a member edge to `mod`. + * Holds if `ref` is a use of a node that should have an incoming edge from the root + * node labeled `lbl` in the API graph (not including those from API::EntryPoint). */ pragma[nomagic] - private predicate moduleScope(DataFlow::ModuleNode mod, Node pred, string name) { - exists(Namespace namespace | - name = namespace.getName() and - namespace = mod.getADeclaration() + private predicate useRoot(Label::ApiLabel lbl, DataFlow::Node ref) { + exists(string name, ConstantReadAccess read | + read = ref.asExpr().getExpr() and + lbl = Label::member(read.getName()) | - exists(DataFlow::Node scopeNode | - scopeNode.asExpr().getExpr() = namespace.getScopeExpr() and - pred = getForwardEndNode(getALocalSourceStrict(scopeNode)) - ) + name = resolveTopLevel(read) or - not exists(namespace.getScopeExpr()) and - if namespace.hasGlobalScope() or namespace.getEnclosingModule() instanceof Toplevel - then pred = MkRoot() - else pred = MkModuleObjectDown(namespace.getEnclosingModule().getModule()) + name = read.getName() and + not exists(resolveTopLevel(read)) and + not exists(read.getScopeExpr()) ) } - cached - predicate memberEdge(Node pred, string name, Node succ) { - exists(ConstantReadAccess read | succ = getForwardStartNode(getNodeFromExpr(read)) | - name = resolveTopLevel(read) and - pred = MkRoot() - or + /** + * Holds if `ref` is a use of a node that should have an incoming edge labeled `lbl`, + * from a use node that flows to `node`. + */ + private predicate useStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node ref) { + // // Referring to an attribute on a node that is a use of `base`: + // pred = `Rails` part of `Rails::Whatever` + // lbl = `Whatever` + // ref = `Rails::Whatever` + exists(ExprNodes::ConstantAccessCfgNode c, ConstantReadAccess read | not exists(resolveTopLevel(read)) and - not exists(read.getScopeExpr()) and - name = read.getName() and - pred = MkRoot() - or - pred = getForwardEndNode(getALocalSourceStrict(getNodeFromExpr(read.getScopeExpr()))) and - name = read.getName() + node.asExpr() = c.getScopeExpr() and + lbl = Label::member(read.getName()) and + ref.asExpr() = c and + read = c.getExpr() ) or + exists(TypeTrackerSpecific::TypeTrackerContent c | + TypeTrackerSpecific::basicLoadStep(node, ref, c) and + lbl = Label::content(c.getAStoreContent()) and + not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) + ) + // note: method calls are not handled here as there is no DataFlow::Node for the intermediate MkMethodAccessNode API node + } + + /** + * Holds if `rhs` is a definition of a node that should have an incoming edge labeled `lbl`, + * from a def node that is reachable from `node`. + */ + private predicate defStep(Label::ApiLabel lbl, DataFlow::Node node, DataFlow::Node rhs) { + exists(TypeTrackerSpecific::TypeTrackerContent c | + TypeTrackerSpecific::basicStoreStep(rhs, node, c) and + lbl = Label::content(c.getAStoreContent()) + ) + } + + pragma[nomagic] + private predicate isUse(DataFlow::Node nd) { + useRoot(_, nd) + or + exists(DataFlow::Node node | + useCandFwd().flowsTo(node) and + useStep(_, node, nd) + ) + or + useCandFwd().flowsTo(nd.(DataFlow::CallNode).getReceiver()) + or + parameterStep(_, defCand(), nd) + or + nd = any(EntryPoint entry).getASource() + or + nd = any(EntryPoint entry).getACall() + } + + /** + * Holds if `ref` is a use of node `nd`. + */ + cached + predicate use(TApiNode nd, DataFlow::Node ref) { + nd = MkUse(ref) + or exists(DataFlow::ModuleNode mod | - moduleScope(mod, pred, name) and - (succ = MkModuleObjectDown(mod) or succ = MkModuleObjectUp(mod)) + nd = MkModuleObject(mod) and + ref = mod.getAnImmediateReference() ) } + /** + * Holds if `rhs` is a RHS of node `nd`. + */ cached - predicate topLevelMember(string name, Node node) { memberEdge(root(), name, node) } + predicate def(TApiNode nd, DataFlow::Node rhs) { nd = MkDef(rhs) } - cached - predicate toplevelCall(string name, Node node) { - exists(DataFlow::CallNode call | - call.asExpr().getExpr().getCfgScope() instanceof Toplevel and - call.getMethodName() = name and - node = MkMethodAccessNode(call) + /** Gets a node reachable from a use-node. */ + private DataFlow::LocalSourceNode useCandFwd(TypeTracker t) { + t.start() and + isUse(result) + or + exists(TypeTracker t2 | result = useCandFwd(t2).track(t2, t)) + } + + /** Gets a node reachable from a use-node. */ + private DataFlow::LocalSourceNode useCandFwd() { result = useCandFwd(TypeTracker::end()) } + + private predicate isDef(DataFlow::Node rhs) { + // If a call node is relevant as a use-node, treat its arguments as def-nodes + argumentStep(_, useCandFwd(), rhs) + or + defStep(_, defCand(), rhs) + or + rhs = any(EntryPoint entry).getASink() + } + + /** Gets a data flow node that flows to the RHS of a def-node. */ + private DataFlow::LocalSourceNode defCand(TypeBackTracker t) { + t.start() and + exists(DataFlow::Node rhs | + isDef(rhs) and + result = rhs.getALocalSource() + ) + or + exists(TypeBackTracker t2 | result = defCand(t2).backtrack(t2, t)) + } + + /** Gets a data flow node that flows to the RHS of a def-node. */ + private DataFlow::LocalSourceNode defCand() { result = defCand(TypeBackTracker::end()) } + + /** + * Holds if there should be a `lbl`-edge from the given call to an argument. + */ + pragma[nomagic] + private predicate argumentStep( + Label::ApiLabel lbl, DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument + ) { + exists(DataFlowDispatch::ArgumentPosition argPos | + argument.sourceArgumentOf(call.asExpr(), argPos) and + lbl = Label::getLabelFromArgumentPosition(argPos) ) } - cached - predicate anyMemberEdge(Node pred, Node succ) { memberEdge(pred, _, succ) } + /** + * Holds if there should be a `lbl`-edge from the given callable to a parameter. + */ + pragma[nomagic] + private predicate parameterStep( + Label::ApiLabel lbl, DataFlow::Node callable, DataFlowPrivate::ParameterNodeImpl paramNode + ) { + exists(DataFlowDispatch::ParameterPosition paramPos | + paramNode.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and + lbl = Label::getLabelFromParameterPosition(paramPos) + ) + } + /** + * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. + * + * The flow from `src` to the returned node may be inter-procedural. + */ + private DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src, TypeTracker t) { + result = src and + isUse(src) and + t.start() + or + exists(TypeTracker t2 | + result = trackUseNode(src, t2).track(t2, t) and + not result instanceof DataFlow::SelfParameterNode + ) + } + + /** + * Gets a data-flow node to which `src`, which is a use of an API-graph node, flows. + * + * The flow from `src` to the returned node may be inter-procedural. + */ cached - predicate methodEdge(Node pred, string name, Node succ) { - exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | - // Treat super calls as if they were calls to the module object/instance. - succ = MkMethodAccessNode(call) and - name = call.getMethodName() - | - pred = MkModuleObjectDown(mod) and - call = mod.getAnOwnSingletonMethod().getASuperCall() + DataFlow::LocalSourceNode trackUseNode(DataFlow::LocalSourceNode src) { + result = trackUseNode(src, TypeTracker::end()) + } + + /** Gets a data flow node reaching the RHS of the given def node. */ + private DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs, TypeBackTracker t) { + t.start() and + isDef(rhs) and + result = rhs.getALocalSource() + or + exists(TypeBackTracker t2, DataFlow::LocalSourceNode mid | + mid = trackDefNode(rhs, t2) and + not mid instanceof DataFlow::SelfParameterNode and + result = mid.backtrack(t2, t) + ) + } + + /** Gets a data flow node reaching the RHS of the given def node. */ + cached + DataFlow::LocalSourceNode trackDefNode(DataFlow::Node rhs) { + result = trackDefNode(rhs, TypeBackTracker::end()) + } + + pragma[nomagic] + private predicate useNodeReachesReceiver(DataFlow::Node use, DataFlow::CallNode call) { + trackUseNode(use).flowsTo(call.getReceiver()) + } + + /** + * Holds if `superclass` is the superclass of `mod`. + */ + pragma[nomagic] + private predicate superclassNode(DataFlow::ModuleNode mod, DataFlow::Node superclass) { + superclass.asExpr().getExpr() = mod.getADeclaration().(ClassDeclaration).getSuperclassExpr() + } + + /** + * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. + */ + cached + predicate edge(TApiNode pred, Label::ApiLabel lbl, TApiNode succ) { + /* Every node that is a use of an API component is itself added to the API graph. */ + exists(DataFlow::LocalSourceNode ref | succ = MkUse(ref) | + pred = MkRoot() and + useRoot(lbl, ref) or - pred = MkModuleInstanceUp(mod) and - call = mod.getAnOwnInstanceMethod().getASuperCall() + exists(DataFlow::Node node, DataFlow::Node src | + use(pred, src) and + trackUseNode(src).flowsTo(node) and + useStep(lbl, node, ref) + ) + or + exists(DataFlow::Node callback | + def(pred, callback) and + parameterStep(lbl, trackDefNode(callback), ref) + ) + ) + or + exists(DataFlow::Node predNode, DataFlow::Node succNode | + def(pred, predNode) and + succ = MkDef(succNode) and + defStep(lbl, trackDefNode(predNode), succNode) + ) + or + exists(DataFlow::Node predNode, DataFlow::Node superclassNode, DataFlow::ModuleNode mod | + use(pred, predNode) and + trackUseNode(predNode).flowsTo(superclassNode) and + superclassNode(mod, superclassNode) and + succ = MkModuleObject(mod) and + lbl = Label::subclass() ) or exists(DataFlow::CallNode call | // from receiver to method call node - pred = getForwardEndNode(getALocalSourceStrict(call.getReceiver())) and - succ = MkMethodAccessNode(call) and - name = call.getMethodName() + exists(DataFlow::Node receiver | + use(pred, receiver) and + useNodeReachesReceiver(receiver, call) and + lbl = Label::method(call.getMethodName()) and + succ = MkMethodAccessNode(call) + ) + or + // from method call node to return and arguments + pred = MkMethodAccessNode(call) and + ( + lbl = Label::return() and + succ = MkUse(call) + or + exists(DataFlow::Node rhs | + argumentStep(lbl, call, rhs) and + succ = MkDef(rhs) + ) + ) ) or - exists(DataFlow::ModuleNode mod | - (pred = MkModuleObjectDown(mod) or pred = MkModuleObjectUp(mod)) and - succ = getBackwardStartNode(mod.getOwnSingletonMethod(name)) - or - pred = MkModuleInstanceUp(mod) and - succ = getBackwardStartNode(mod.getOwnInstanceMethod(name)) - ) - } - - cached - predicate asCallable(Node apiNode, DataFlow::CallableNode callable) { - apiNode = getBackwardStartNode(callable) - } - - cached - predicate contentEdge(Node pred, DataFlow::Content content, Node succ) { - exists( - DataFlow::Node object, DataFlow::Node value, TypeTrackerSpecific::TypeTrackerContent c + exists(EntryPoint entry | + pred = root() and + lbl = Label::entryPoint(entry) | - TypeTrackerSpecific::basicLoadStep(object, value, c) and - content = c.getAStoreContent() and - not c.isSingleton(any(DataFlow::Content::AttributeNameContent k)) and - // `x -> x.foo` with content "foo" - pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and - succ = getForwardStartNode(value) + succ = MkDef(entry.getASink()) or - // Based on `object.c = value` generate `object -> value` with content `c` - TypeTrackerSpecific::basicStoreStep(value, object, c) and - content = c.getAStoreContent() and - pred = getForwardOrBackwardEndNode(getALocalSourceStrict(object)) and - succ = MkSinkNode(value) + succ = MkUse(entry.getASource()) + or + succ = MkMethodAccessNode(entry.getACall()) ) } + /** + * Holds if there is an edge from `pred` to `succ` in the API graph. + */ + private predicate edge(TApiNode pred, TApiNode succ) { edge(pred, _, succ) } + + /** Gets the shortest distance from the root to `nd` in the API graph. */ cached - predicate fieldEdge(Node pred, string name, Node succ) { - Impl::contentEdge(pred, DataFlowPrivate::TFieldContent(name), succ) - } + int distanceFromRoot(TApiNode nd) = shortestDistances(MkRoot/0, edge/2)(_, nd, result) - cached - predicate elementEdge(Node pred, Node succ) { - contentEdge(pred, any(DataFlow::ContentSet set | set.isAnyElement()).getAReadContent(), succ) - } - - cached - predicate parameterEdge(Node pred, DataFlowDispatch::ParameterPosition paramPos, Node succ) { - exists(DataFlowPrivate::ParameterNodeImpl parameter, DataFlow::CallableNode callable | - parameter.isSourceParameterOf(callable.asExpr().getExpr(), paramPos) and - pred = getBackwardEndNode(callable) and - succ = getForwardStartNode(parameter) - ) - } - - cached - predicate argumentEdge(Node pred, DataFlowDispatch::ArgumentPosition argPos, Node succ) { - exists(DataFlow::CallNode call, DataFlowPrivate::ArgumentNode argument | - argument.sourceArgumentOf(call.asExpr(), argPos) and - pred = MkMethodAccessNode(call) and - succ = MkSinkNode(argument) - ) - } - - cached - predicate positionalArgumentEdge(Node pred, int n, Node succ) { - argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isPositional(n)), succ) - } - - cached - predicate keywordArgumentEdge(Node pred, string name, Node succ) { - argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isKeyword(name)), succ) - } - - private predicate blockArgumentEdge(Node pred, Node succ) { - argumentEdge(pred, any(DataFlowDispatch::ArgumentPosition pos | pos.isBlock()), succ) - } - - private predicate positionalParameterEdge(Node pred, int n, Node succ) { - parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isPositional(n)), succ) - } - - private predicate keywordParameterEdge(Node pred, string name, Node succ) { - parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isKeyword(name)), succ) - } - - cached - predicate blockParameterEdge(Node pred, Node succ) { - parameterEdge(pred, any(DataFlowDispatch::ParameterPosition pos | pos.isBlock()), succ) - } - - cached - predicate positionalParameterOrArgumentEdge(Node pred, int n, Node succ) { - positionalArgumentEdge(pred, n, succ) - or - positionalParameterEdge(pred, n, succ) - } - - cached - predicate keywordParameterOrArgumentEdge(Node pred, string name, Node succ) { - keywordArgumentEdge(pred, name, succ) - or - keywordParameterEdge(pred, name, succ) - } - - cached - predicate blockParameterOrArgumentEdge(Node pred, Node succ) { - blockArgumentEdge(pred, succ) - or - blockParameterEdge(pred, succ) - } - - pragma[nomagic] - private predicate newCall(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call) { - call = receiver.getAMethodCall("new") - } - - cached - predicate instanceEdge(Node pred, Node succ) { - exists(DataFlow::ModuleNode mod | - pred = MkModuleObjectDown(mod) and - succ = MkModuleInstanceUp(mod) - ) - or - exists(DataFlow::LocalSourceNode receiver, DataFlow::CallNode call | - newCall(receiver, call) and - pred = getForwardEndNode(receiver) and - succ = getForwardStartNode(call) - ) - } - - cached - predicate returnEdge(Node pred, Node succ) { - exists(DataFlow::CallNode call | - pred = MkMethodAccessNode(call) and - succ = getForwardStartNode(call) - ) - or - exists(DataFlow::CallableNode callable | - pred = getBackwardEndNode(callable) and - succ = MkSinkNode(callable.getAReturnNode()) - ) - } - - cached - predicate entryPointEdge(EntryPoint entry, Node node) { - node = MkSinkNode(entry.getASink()) or - node = getForwardStartNode(entry.getASource()) or - node = MkMethodAccessNode(entry.getACall()) - } - } - - /** - * Holds if there is an edge from `pred` to `succ` in the API graph that is labeled with `lbl`. - */ - pragma[nomagic] - deprecated private predicate labelledEdge(Node pred, Label::ApiLabel lbl, Node succ) { - exists(string name | - Impl::memberEdge(pred, name, succ) and - lbl = Label::member(name) - ) - or - exists(string name | - Impl::methodEdge(pred, name, succ) and - lbl = Label::method(name) - ) - or - exists(DataFlow::Content content | - Impl::contentEdge(pred, content, succ) and - lbl = Label::content(content) - ) - or - exists(DataFlowDispatch::ParameterPosition pos | - Impl::parameterEdge(pred, pos, succ) and - lbl = Label::getLabelFromParameterPosition(pos) - ) - or - exists(DataFlowDispatch::ArgumentPosition pos | - Impl::argumentEdge(pred, pos, succ) and - lbl = Label::getLabelFromArgumentPosition(pos) - ) - or - Impl::instanceEdge(pred, succ) and - lbl = Label::instance() - or - Impl::returnEdge(pred, succ) and - lbl = Label::return() - or - exists(EntryPoint entry | - Impl::entryPointEdge(entry, succ) and - pred = root() and - lbl = Label::entryPoint(entry) - ) - } - - /** - * DEPRECATED. Treating the API graph as an explicit labelled graph is deprecated - instead use the methods on `API:Node` directly. - * - * Provides classes modeling the various edges (labels) in the API graph. - */ - deprecated module Label { /** All the possible labels in the API graph. */ - private newtype TLabel = + cached + newtype TLabel = MkLabelMember(string member) { member = any(ConstantReadAccess a).getName() } or MkLabelMethod(string m) { m = any(DataFlow::CallNode c).getMethodName() } or MkLabelReturn() or - MkLabelInstance() or + MkLabelSubclass() or MkLabelKeywordParameter(string name) { any(DataFlowDispatch::ArgumentPosition arg).isKeyword(name) or @@ -1424,9 +923,12 @@ module API { MkLabelBlockParameter() or MkLabelEntryPoint(EntryPoint name) or MkLabelContent(DataFlow::Content content) + } + /** Provides classes modeling the various edges (labels) in the API graph. */ + module Label { /** A label in the API-graph */ - class ApiLabel extends TLabel { + class ApiLabel extends Impl::TLabel { /** Gets a string representation of this label. */ string toString() { result = "???" } } @@ -1465,9 +967,9 @@ module API { override string toString() { result = "getReturn()" } } - /** A label for getting instances of a module/class. */ - class LabelInstance extends ApiLabel, MkLabelInstance { - override string toString() { result = "getInstance()" } + /** A label for the subclass relationship. */ + class LabelSubclass extends ApiLabel, MkLabelSubclass { + override string toString() { result = "getASubclass()" } } /** A label for a keyword parameter. */ @@ -1535,8 +1037,8 @@ module API { /** Gets the `return` edge label. */ LabelReturn return() { any() } - /** Gets the `instance` edge label. */ - LabelInstance instance() { any() } + /** Gets the `subclass` edge label. */ + LabelSubclass subclass() { any() } /** Gets the label representing the given keyword argument/parameter. */ LabelKeywordParameter keywordParameter(string name) { result.getName() = name } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 7772e5235d7..a98238e85d9 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -6,17 +6,12 @@ private import codeql.ruby.typetracking.TypeTracker private import codeql.ruby.dataflow.SSA private import FlowSummaryImpl as FlowSummaryImpl private import SsaImpl as SsaImpl -private import codeql.ruby.ApiGraphs /** * An element, viewed as a node in a data flow graph. Either an expression * (`ExprNode`) or a parameter (`ParameterNode`). */ class Node extends TNode { - /** Starts backtracking from this node using API graphs. */ - pragma[inline] - API::Node backtrack() { result = API::Internal::getNodeForBacktracking(this) } - /** Gets the expression corresponding to this node, if any. */ CfgNodes::ExprCfgNode asExpr() { result = this.(ExprNode).getExprNode() } @@ -81,11 +76,6 @@ class Node extends TNode { result.asCallableAstNode() = c.asCallable() ) } - - /** Gets the enclosing method, if any. */ - MethodNode getEnclosingMethod() { - result.asCallableAstNode() = this.asExpr().getExpr().getEnclosingMethod() - } } /** A data-flow node corresponding to a call in the control-flow graph. */ @@ -154,18 +144,6 @@ class CallNode extends LocalSourceNode, ExprNode { result.asExpr() = pair.getValue() ) } - - /** - * Gets a potential target of this call, if any. - */ - final CallableNode getATarget() { - result.asCallableAstNode() = this.asExpr().getExpr().(Call).getATarget() - } - - /** - * Holds if this is a `super` call. - */ - final predicate isSuperCall() { this.asExpr().getExpr() instanceof SuperCall } } /** @@ -239,10 +217,6 @@ class SelfParameterNode extends ParameterNode instanceof SelfParameterNodeImpl { class LocalSourceNode extends Node { LocalSourceNode() { isLocalSourceNode(this) } - /** Starts tracking this node forward using API graphs. */ - pragma[inline] - API::Node track() { result = API::Internal::getNodeForForwardTracking(this) } - /** Holds if this `LocalSourceNode` can flow to `nodeTo` in one or more local flow steps. */ pragma[inline] predicate flowsTo(Node nodeTo) { hasLocalSource(nodeTo, this) } @@ -385,11 +359,6 @@ private module Cached { ) } - cached - predicate methodHasSuperCall(MethodNode method, CallNode call) { - call.isSuperCall() and method = call.getEnclosingMethod() - } - /** * A place in which a named constant can be looked up during constant lookup. */ @@ -418,39 +387,6 @@ private module Cached { result.asExpr().getExpr() = access } - /** - * Gets a module for which `constRef` is the reference to an ancestor module. - * - * For example, `M` is the ancestry target of `C` in the following examples: - * ```rb - * class M < C {} - * - * module M - * include C - * end - * - * module M - * prepend C - * end - * ``` - */ - private ModuleNode getAncestryTarget(ConstRef constRef) { result.getAnAncestorExpr() = constRef } - - /** - * Gets a scope in which a constant lookup may access the contents of the module referenced by `constRef`. - */ - cached - TConstLookupScope getATargetScope(ConstRef constRef) { - result = MkAncestorLookup(getAncestryTarget(constRef).getAnImmediateDescendent*()) - or - constRef.asConstantAccess() = any(ConstantAccess ac).getScopeExpr() and - result = MkQualifiedLookup(constRef.asConstantAccess()) - or - result = MkNestedLookup(getAncestryTarget(constRef)) - or - result = MkExactLookup(constRef.asConstantAccess().(Namespace).getModule()) - } - cached predicate forceCachingInSameStage() { any() } @@ -1092,33 +1028,6 @@ class ModuleNode instanceof Module { * this predicate. */ ModuleNode getNestedModule(string name) { result = super.getNestedModule(name) } - - /** - * Starts tracking the module object using API graphs. - * - * Concretely, this tracks forward from the following starting points: - * - A constant access that resolves to this module. - * - `self` in the module scope or in a singleton method of the module. - * - A call to `self.class` in an instance method of this module or an ancestor module. - */ - bindingset[this] - pragma[inline] - API::Node trackModule() { result = API::Internal::getModuleNode(this) } - - /** - * Starts tracking instances of this module forward using API graphs. - * - * Concretely, this tracks forward from the following starting points: - * - `self` in instance methods of this module and ancestor modules - * - Calls to `new` on the module object - * - * Note that this includes references to `self` in ancestor modules, but not in descendent modules. - * This is usually the desired behavior, particularly if this module was itself found using - * a call to `getADescendentModule()`. - */ - bindingset[this] - pragma[inline] - API::Node trackInstance() { result = API::Internal::getModuleInstance(this) } } /** @@ -1307,9 +1216,6 @@ class MethodNode extends CallableNode { /** Holds if this method is protected. */ predicate isProtected() { this.asCallableAstNode().isProtected() } - - /** Gets a `super` call in this method. */ - CallNode getASuperCall() { methodHasSuperCall(this, result) } } /** @@ -1428,6 +1334,24 @@ class ConstRef extends LocalSourceNode { not exists(access.getScopeExpr()) } + /** + * Gets a module for which this constant is the reference to an ancestor module. + * + * For example, `M` is the ancestry target of `C` in the following examples: + * ```rb + * class M < C {} + * + * module M + * include C + * end + * + * module M + * prepend C + * end + * ``` + */ + private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this } + /** * Gets the known target module. * @@ -1435,6 +1359,22 @@ class ConstRef extends LocalSourceNode { */ private Module getExactTarget() { result.getAnImmediateReference() = access } + /** + * Gets a scope in which a constant lookup may access the contents of the module referenced by this constant. + */ + cached + private TConstLookupScope getATargetScope() { + forceCachingInSameStage() and + result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*()) + or + access = any(ConstantAccess ac).getScopeExpr() and + result = MkQualifiedLookup(access) + or + result = MkNestedLookup(this.getAncestryTarget()) + or + result = MkExactLookup(access.(Namespace).getModule()) + } + /** * Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes). * @@ -1496,7 +1436,7 @@ class ConstRef extends LocalSourceNode { pragma[inline] ConstRef getConstant(string name) { exists(TConstLookupScope scope | - pragma[only_bind_into](scope) = getATargetScope(pragma[only_bind_out](this)) and + pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and result.accesses(pragma[only_bind_out](scope), name) ) } @@ -1518,9 +1458,7 @@ class ConstRef extends LocalSourceNode { * end * ``` */ - bindingset[this] - pragma[inline_late] - ModuleNode getADescendentModule() { MkAncestorLookup(result) = getATargetScope(this) } + ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() } } /** diff --git a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll index 449e05ad9e8..5c24978c4c3 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/UnicodeBypassValidationQuery.qll @@ -91,19 +91,19 @@ class Configuration extends TaintTracking::Configuration { // unicode_utils exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UnicodeUtils").getMethod(["nfkd", "nfc", "nfd", "nfkc"]) and - sink = mac.getArgument(0).asSink() + sink = mac.getParameter(0).asSink() ) or // eprun exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("Eprun").getMethod("normalize") and - sink = mac.getArgument(0).asSink() + sink = mac.getParameter(0).asSink() ) or // unf exists(API::MethodAccessNode mac | mac = API::getTopLevelMember("UNF").getMember("Normalizer").getMethod("normalize") and - sink = mac.getArgument(0).asSink() + sink = mac.getParameter(0).asSink() ) or // ActiveSupport::Multibyte::Chars @@ -113,7 +113,7 @@ class Configuration extends TaintTracking::Configuration { .getMember("Multibyte") .getMember("Chars") .getMethod("new") - .asCall() and + .getCallNode() and n = cn.getAMethodCall("normalize") and sink = cn.getArgument(0) ) diff --git a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll index 656ceedbe2b..e94cabb414c 100644 --- a/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll +++ b/ruby/ql/lib/codeql/ruby/experimental/ZipSlipCustomizations.qll @@ -89,7 +89,7 @@ module ZipSlip { // If argument refers to a string object, then it's a hardcoded path and // this file is safe. not zipOpen - .asCall() + .getCallNode() .getArgument(0) .getALocalSource() .getConstantValue() diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll index a687837f8fd..31bdc42e350 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionController.qll @@ -83,8 +83,8 @@ class ActionControllerClass extends DataFlow::ClassNode { } } -private API::Node actionControllerInstance() { - result = any(ActionControllerClass cls).getSelf().track() +private DataFlow::LocalSourceNode actionControllerInstance() { + result = any(ActionControllerClass cls).getSelf() } /** @@ -222,19 +222,19 @@ private class ActionControllerRenderToCall extends RenderToCallImpl { } } -pragma[nomagic] -private DataFlow::CallNode renderCall() { - // ActionController#render is an alias for ActionController::Renderer#render - result = - [ - any(ActionControllerClass c).trackModule().getAMethodCall("render"), - any(ActionControllerClass c).trackModule().getReturn("renderer").getAMethodCall("render") - ] -} - /** A call to `ActionController::Renderer#render`. */ private class RendererRenderCall extends RenderCallImpl { - RendererRenderCall() { this = renderCall().asExpr().getExpr() } + RendererRenderCall() { + this = + [ + // ActionController#render is an alias for ActionController::Renderer#render + any(ActionControllerClass c).getAnImmediateReference().getAMethodCall("render"), + any(ActionControllerClass c) + .getAnImmediateReference() + .getAMethodCall("renderer") + .getAMethodCall("render") + ].asExpr().getExpr() + } } /** A call to `html_escape` from within a controller. */ @@ -260,7 +260,6 @@ class RedirectToCall extends MethodCall { this = controller .getSelf() - .track() .getAMethodCall(["redirect_to", "redirect_back", "redirect_back_or_to"]) .asExpr() .getExpr() @@ -601,7 +600,9 @@ private module ParamsSummaries { * response. */ private module Response { - API::Node response() { result = actionControllerInstance().getReturn("response") } + DataFlow::LocalSourceNode response() { + result = actionControllerInstance().getAMethodCall("response") + } class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range { BodyWrite() { this = response().getAMethodCall("body=") } @@ -627,7 +628,7 @@ private module Response { HeaderWrite() { // response.header[key] = val // response.headers[key] = val - this = response().getReturn(["header", "headers"]).getAMethodCall("[]=") + this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=") or // response.set_header(key) = val // response[header] = val @@ -672,12 +673,18 @@ private module Response { } } +private class ActionControllerLoggerInstance extends DataFlow::Node { + ActionControllerLoggerInstance() { + this = actionControllerInstance().getAMethodCall("logger") + or + any(ActionControllerLoggerInstance i).(DataFlow::LocalSourceNode).flowsTo(this) + } +} + private class ActionControllerLoggingCall extends DataFlow::CallNode, Logging::Range { ActionControllerLoggingCall() { - this = - actionControllerInstance() - .getReturn("logger") - .getAMethodCall(["debug", "error", "fatal", "info", "unknown", "warn"]) + this.getReceiver() instanceof ActionControllerLoggerInstance and + this.getMethodName() = ["debug", "error", "fatal", "info", "unknown", "warn"] } // Note: this is identical to the definition `stdlib.Logger.LoggerInfoStyleCall`. diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll index f237e42a9a9..13607f67926 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailbox.qll @@ -27,8 +27,8 @@ module ActionMailbox { Mail() { this = [ - controller().trackInstance().getReturn("inbound_email").getAMethodCall("mail"), - controller().trackInstance().getAMethodCall("mail") + controller().getAnInstanceSelf().getAMethodCall("inbound_email").getAMethodCall("mail"), + controller().getAnInstanceSelf().getAMethodCall("mail") ] } } @@ -40,7 +40,7 @@ module ActionMailbox { RemoteContent() { this = any(Mail m) - .track() + .(DataFlow::LocalSourceNode) .getAMethodCall([ "body", "to", "from", "raw_source", "subject", "from_address", "recipients_addresses", "cc_addresses", "bcc_addresses", "in_reply_to", diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll index 4884f46aac3..af183333d3d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActionMailer.qll @@ -4,26 +4,12 @@ private import codeql.ruby.AST private import codeql.ruby.ApiGraphs -private import codeql.ruby.DataFlow private import codeql.ruby.frameworks.internal.Rails /** * Provides modeling for the `ActionMailer` library. */ module ActionMailer { - private DataFlow::ClassNode actionMailerClass() { - result = - [ - DataFlow::getConstant("ActionMailer").getConstant("Base"), - // In Rails applications `ApplicationMailer` typically extends - // `ActionMailer::Base`, but we treat it separately in case the - // `ApplicationMailer` definition is not in the database. - DataFlow::getConstant("ApplicationMailer") - ].getADescendentModule() - } - - private API::Node actionMailerInstance() { result = actionMailerClass().trackInstance() } - /** * A `ClassDeclaration` for a class that extends `ActionMailer::Base`. * For example, @@ -35,11 +21,33 @@ module ActionMailer { * ``` */ class MailerClass extends ClassDeclaration { - MailerClass() { this = actionMailerClass().getADeclaration() } + MailerClass() { + this.getSuperclassExpr() = + [ + API::getTopLevelMember("ActionMailer").getMember("Base"), + // In Rails applications `ApplicationMailer` typically extends + // `ActionMailer::Base`, but we treat it separately in case the + // `ApplicationMailer` definition is not in the database. + API::getTopLevelMember("ApplicationMailer") + ].getASubclass().getAValueReachableFromSource().asExpr().getExpr() + } + } + + /** A method call with a `self` receiver from within a mailer class */ + private class ContextCall extends MethodCall { + private MailerClass mailerClass; + + ContextCall() { + this.getReceiver() instanceof SelfVariableAccess and + this.getEnclosingModule() = mailerClass + } + + /** Gets the mailer class containing this method. */ + MailerClass getMailerClass() { result = mailerClass } } /** A call to `params` from within a mailer. */ - class ParamsCall extends ParamsCallImpl { - ParamsCall() { this = actionMailerInstance().getAMethodCall("params").asExpr().getExpr() } + class ParamsCall extends ContextCall, ParamsCallImpl { + ParamsCall() { this.getMethodName() = "params" } } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll index 4a5d342eeba..fcca078f933 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveRecord.qll @@ -30,8 +30,10 @@ private predicate isBuiltInMethodForActiveRecordModelInstance(string methodName) methodName = objectInstanceMethodName() } -private API::Node activeRecordBaseClass() { +private API::Node activeRecordClassApiNode() { result = + // class Foo < ActiveRecord::Base + // class Bar < Foo [ API::getTopLevelMember("ActiveRecord").getMember("Base"), // In Rails applications `ApplicationRecord` typically extends `ActiveRecord::Base`, but we @@ -40,46 +42,6 @@ private API::Node activeRecordBaseClass() { ] } -/** - * Gets an object with methods from the ActiveRecord query interface. - */ -private API::Node activeRecordQueryBuilder() { - result = activeRecordBaseClass() - or - result = activeRecordBaseClass().getInstance() - or - // Assume any method call might return an ActiveRecord::Relation - // These are dynamically generated - result = activeRecordQueryBuilderMethodAccess(_).getReturn() -} - -/** Gets a call targeting the ActiveRecord query interface. */ -private API::MethodAccessNode activeRecordQueryBuilderMethodAccess(string name) { - result = activeRecordQueryBuilder().getMethod(name) and - // Due to the heuristic tracking of query builder objects, add a restriction for methods with a known call target - not isUnlikelyExternalCall(result) -} - -/** Gets a call targeting the ActiveRecord query interface. */ -private DataFlow::CallNode activeRecordQueryBuilderCall(string name) { - result = activeRecordQueryBuilderMethodAccess(name).asCall() -} - -/** - * Holds if `call` is unlikely to call into an external library, since it has a possible - * call target in its enclosing module. - */ -private predicate isUnlikelyExternalCall(API::MethodAccessNode node) { - exists(DataFlow::ModuleNode mod, DataFlow::CallNode call | call = node.asCall() | - call.getATarget() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] and - call.getEnclosingMethod() = [mod.getAnOwnSingletonMethod(), mod.getAnOwnInstanceMethod()] - ) -} - -private API::Node activeRecordConnectionInstance() { - result = activeRecordBaseClass().getReturn("connection") -} - /** * A `ClassDeclaration` for a class that inherits from `ActiveRecord::Base`. For example, * @@ -93,19 +55,20 @@ private API::Node activeRecordConnectionInstance() { * ``` */ class ActiveRecordModelClass extends ClassDeclaration { - private DataFlow::ClassNode cls; - ActiveRecordModelClass() { - cls = activeRecordBaseClass().getADescendentModule() and this = cls.getADeclaration() + this.getSuperclassExpr() = + activeRecordClassApiNode().getASubclass().getAValueReachableFromSource().asExpr().getExpr() } // Gets the class declaration for this class and all of its super classes - private ModuleBase getAllClassDeclarations() { result = cls.getAnAncestor().getADeclaration() } + private ModuleBase getAllClassDeclarations() { + result = this.getModule().getSuperClass*().getADeclaration() + } /** * Gets methods defined in this class that may access a field from the database. */ - deprecated Method getAPotentialFieldAccessMethod() { + Method getAPotentialFieldAccessMethod() { // It's a method on this class or one of its super classes result = this.getAllClassDeclarations().getAMethod() and // There is a value that can be returned by this method which may include field data @@ -127,84 +90,58 @@ class ActiveRecordModelClass extends ClassDeclaration { ) ) } - - /** Gets the class as a `DataFlow::ClassNode`. */ - DataFlow::ClassNode getClassNode() { result = cls } } -/** - * Gets a potential reference to an ActiveRecord class object. - */ -deprecated private API::Node getAnActiveRecordModelClassRef() { - result = any(ActiveRecordModelClass cls).getClassNode().trackModule() - or - // For methods with an unknown call target, assume this might be a database field, thus returning another ActiveRecord object. - // In this case we do not know which class it belongs to, which is why this predicate can't associate the reference with a specific class. - result = getAnUnknownActiveRecordModelClassCall().getReturn() -} +/** A class method call whose receiver is an `ActiveRecordModelClass`. */ +class ActiveRecordModelClassMethodCall extends MethodCall { + private ActiveRecordModelClass recvCls; -/** - * Gets a call performed on an ActiveRecord class object, without a known call target in the codebase. - */ -deprecated private API::MethodAccessNode getAnUnknownActiveRecordModelClassCall() { - result = getAnActiveRecordModelClassRef().getMethod(_) and - result.asCall().asExpr().getExpr() instanceof UnknownMethodCall -} - -/** - * DEPRECATED. Use `ActiveRecordModelClass.getClassNode().trackModule().getMethod()` instead. - * - * A class method call whose receiver is an `ActiveRecordModelClass`. - */ -deprecated class ActiveRecordModelClassMethodCall extends MethodCall { ActiveRecordModelClassMethodCall() { - this = getAnUnknownActiveRecordModelClassCall().asCall().asExpr().getExpr() + // e.g. Foo.where(...) + recvCls.getModule() = this.getReceiver().(ConstantReadAccess).getModule() + or + // e.g. Foo.joins(:bars).where(...) + recvCls = this.getReceiver().(ActiveRecordModelClassMethodCall).getReceiverClass() + or + // e.g. self.where(...) within an ActiveRecordModelClass + this.getReceiver() instanceof SelfVariableAccess and + this.getEnclosingModule() = recvCls } - /** Gets the `ActiveRecordModelClass` of the receiver of this method, if it can be determined. */ - ActiveRecordModelClass getReceiverClass() { - this = result.getClassNode().trackModule().getMethod(_).asCall().asExpr().getExpr() - } + /** The `ActiveRecordModelClass` of the receiver of this method. */ + ActiveRecordModelClass getReceiverClass() { result = recvCls } } -private predicate sqlFragmentArgumentInner(DataFlow::CallNode call, DataFlow::Node sink) { - call = - activeRecordQueryBuilderCall([ - "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", - "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", - "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", "rewhere", - "select", "reselect", "update_all" - ]) and - sink = call.getArgument(0) - or - call = activeRecordQueryBuilderCall("calculate") and - sink = call.getArgument(1) - or - call = - activeRecordQueryBuilderCall(["average", "count", "maximum", "minimum", "sum", "count_by_sql"]) and - sink = call.getArgument(0) - or - // This format was supported until Rails 2.3.8 - call = activeRecordQueryBuilderCall(["all", "find", "first", "last"]) and - sink = call.getKeywordArgument("conditions") - or - call = activeRecordQueryBuilderCall("reload") and - sink = call.getKeywordArgument("lock") - or - // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to - // SQLi if user supplied input is passed in as an argument. - call = activeRecordQueryBuilderCall("annotate") and - sink = call.getArgument(_) - or - call = activeRecordConnectionInstance().getAMethodCall("execute") and - sink = call.getArgument(0) -} - -private predicate sqlFragmentArgument(DataFlow::CallNode call, DataFlow::Node sink) { - exists(DataFlow::Node arg | - sqlFragmentArgumentInner(call, arg) and - sink = [arg, arg.(DataFlow::ArrayLiteralNode).getElement(0)] and - unsafeSqlExpr(sink.asExpr().getExpr()) +private Expr sqlFragmentArgument(MethodCall call) { + exists(string methodName | + methodName = call.getMethodName() and + ( + methodName = + [ + "delete_all", "delete_by", "destroy_all", "destroy_by", "exists?", "find_by", "find_by!", + "find_or_create_by", "find_or_create_by!", "find_or_initialize_by", "find_by_sql", "from", + "group", "having", "joins", "lock", "not", "order", "reorder", "pluck", "where", + "rewhere", "select", "reselect", "update_all" + ] and + result = call.getArgument(0) + or + methodName = "calculate" and result = call.getArgument(1) + or + methodName in ["average", "count", "maximum", "minimum", "sum", "count_by_sql"] and + result = call.getArgument(0) + or + // This format was supported until Rails 2.3.8 + methodName = ["all", "find", "first", "last"] and + result = call.getKeywordArgument("conditions") + or + methodName = "reload" and + result = call.getKeywordArgument("lock") + or + // Calls to `annotate` can be used to add block comments to SQL queries. These are potentially vulnerable to + // SQLi if user supplied input is passed in as an argument. + methodName = "annotate" and + result = call.getArgument(_) + ) ) } @@ -225,8 +162,6 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { } /** - * DEPRECATED. Use the `SqlExecution` concept or `ActiveRecordSqlExecutionRange`. - * * A method call that may result in executing unintended user-controlled SQL * queries if the `getSqlFragmentSinkArgument()` expression is tainted by * unsanitized user-controlled input. For example, supposing that `User` is an @@ -240,32 +175,55 @@ private predicate unsafeSqlExpr(Expr sqlFragmentExpr) { * as `"') OR 1=1 --"` could result in the application looking up all users * rather than just one with a matching name. */ -deprecated class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { - private DataFlow::CallNode call; +class PotentiallyUnsafeSqlExecutingMethodCall extends ActiveRecordModelClassMethodCall { + // The SQL fragment argument itself + private Expr sqlFragmentExpr; PotentiallyUnsafeSqlExecutingMethodCall() { - call.asExpr().getExpr() = this and sqlFragmentArgument(call, _) + exists(Expr arg | + arg = sqlFragmentArgument(this) and + unsafeSqlExpr(sqlFragmentExpr) and + ( + sqlFragmentExpr = arg + or + sqlFragmentExpr = arg.(ArrayLiteral).getElement(0) + ) and + // Check that method has not been overridden + not exists(SingletonMethod m | + m.getName() = this.getMethodName() and + m.getOuterScope() = this.getReceiverClass() + ) + ) } /** * Gets the SQL fragment argument of this method call. */ - Expr getSqlFragmentSinkArgument() { - exists(DataFlow::Node sink | - sqlFragmentArgument(call, sink) and result = sink.asExpr().getExpr() - ) - } + Expr getSqlFragmentSinkArgument() { result = sqlFragmentExpr } } /** - * A SQL execution arising from a call to the ActiveRecord library. + * An `SqlExecution::Range` for an argument to a + * `PotentiallyUnsafeSqlExecutingMethodCall` that may be vulnerable to being + * controlled by user input. */ class ActiveRecordSqlExecutionRange extends SqlExecution::Range { - ActiveRecordSqlExecutionRange() { sqlFragmentArgument(_, this) } + ActiveRecordSqlExecutionRange() { + exists(PotentiallyUnsafeSqlExecutingMethodCall mc | + this.asExpr().getNode() = mc.getSqlFragmentSinkArgument() + ) + or + this = activeRecordConnectionInstance().getAMethodCall("execute").getArgument(0) and + unsafeSqlExpr(this.asExpr().getExpr()) + } override DataFlow::Node getSql() { result = this } } +private API::Node activeRecordConnectionInstance() { + result = activeRecordClassApiNode().getReturn("connection") +} + // TODO: model `ActiveRecord` sanitizers // https://api.rubyonrails.org/classes/ActiveRecord/Sanitization/ClassMethods.html /** @@ -283,8 +241,15 @@ abstract class ActiveRecordModelInstantiation extends OrmInstantiation::Range, override predicate methodCallMayAccessField(string methodName) { // The method is not a built-in, and... not isBuiltInMethodForActiveRecordModelInstance(methodName) and - // ...There is no matching method definition in the class - not exists(this.getClass().getMethod(methodName)) + ( + // ...There is no matching method definition in the class, or... + not exists(this.getClass().getMethod(methodName)) + or + // ...the called method can access a field. + exists(Method m | m = this.getClass().getAPotentialFieldAccessMethod() | + m.getName() = methodName + ) + ) } } @@ -352,10 +317,21 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation } // A `self` reference that may resolve to an active record model object -private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation { +private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation, + DataFlow::SelfParameterNode +{ private ActiveRecordModelClass cls; - ActiveRecordModelClassSelfReference() { this = cls.getClassNode().getAnOwnInstanceSelf() } + ActiveRecordModelClassSelfReference() { + exists(MethodBase m | + m = this.getCallable() and + m.getEnclosingModule() = cls and + m = cls.getAMethod() + ) and + // In a singleton method, `self` refers to the class itself rather than an + // instance of that class + not this.getSelfVariable().getDeclaringScope() instanceof SingletonMethod + } final override ActiveRecordModelClass getClass() { result = cls } } @@ -366,7 +342,7 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta class ActiveRecordInstance extends DataFlow::Node { private ActiveRecordModelInstantiation instantiation; - ActiveRecordInstance() { this = instantiation.track().getAValueReachableFromSource() } + ActiveRecordInstance() { this = instantiation or instantiation.flowsTo(this) } /** Gets the `ActiveRecordModelClass` that this is an instance of. */ ActiveRecordModelClass getClass() { result = instantiation.getClass() } @@ -404,12 +380,12 @@ private module Persistence { /** A call to e.g. `User.create(name: "foo")` */ private class CreateLikeCall extends DataFlow::CallNode, PersistentWriteAccess::Range { CreateLikeCall() { - this = - activeRecordBaseClass() - .getAMethodCall([ - "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", - "find_or_create_by!", "insert", "insert!" - ]) + exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and + this.getMethodName() = + [ + "create", "create!", "create_or_find_by", "create_or_find_by!", "find_or_create_by", + "find_or_create_by!", "insert", "insert!" + ] } override DataFlow::Node getValue() { @@ -426,7 +402,8 @@ private module Persistence { /** A call to e.g. `User.update(1, name: "foo")` */ private class UpdateLikeClassMethodCall extends DataFlow::CallNode, PersistentWriteAccess::Range { UpdateLikeClassMethodCall() { - this = activeRecordBaseClass().getAMethodCall(["update", "update!", "upsert"]) + exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and + this.getMethodName() = ["update", "update!", "upsert"] } override DataFlow::Node getValue() { @@ -471,7 +448,10 @@ private module Persistence { * ``` */ private class TouchAllCall extends DataFlow::CallNode, PersistentWriteAccess::Range { - TouchAllCall() { this = activeRecordQueryBuilderCall("touch_all") } + TouchAllCall() { + exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and + this.getMethodName() = "touch_all" + } override DataFlow::Node getValue() { result = this.getKeywordArgument("time") } } @@ -481,7 +461,8 @@ private module Persistence { private ExprNodes::ArrayLiteralCfgNode arr; InsertAllLikeCall() { - this = activeRecordBaseClass().getAMethodCall(["insert_all", "insert_all!", "upsert_all"]) and + exists(this.asExpr().getExpr().(ActiveRecordModelClassMethodCall).getReceiverClass()) and + this.getMethodName() = ["insert_all", "insert_all!", "upsert_all"] and arr = this.getArgument(0).asExpr() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll index 9f0e0f4b859..96219915770 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/ActiveResource.qll @@ -18,12 +18,8 @@ module ActiveResource { * An ActiveResource model class. This is any (transitive) subclass of ActiveResource. */ pragma[nomagic] - private API::Node activeResourceBaseClass() { - result = API::getTopLevelMember("ActiveResource").getMember("Base") - } - - private DataFlow::ClassNode activeResourceClass() { - result = activeResourceBaseClass().getADescendentModule() + private API::Node modelApiNode() { + result = API::getTopLevelMember("ActiveResource").getMember("Base").getASubclass() } /** @@ -34,8 +30,16 @@ module ActiveResource { * end * ``` */ - class ModelClassNode extends DataFlow::ClassNode { - ModelClassNode() { this = activeResourceClass() } + class ModelClass extends ClassDeclaration { + API::Node model; + + ModelClass() { + model = modelApiNode() and + this.getSuperclassExpr() = model.getAValueReachableFromSource().asExpr().getExpr() + } + + /** Gets the API node for this model */ + API::Node getModelApiNode() { result = model } /** Gets a call to `site=`, which sets the base URL for this model. */ SiteAssignCall getASiteAssignment() { result.getModelClass() = this } @@ -45,46 +49,6 @@ module ActiveResource { c = this.getASiteAssignment() and c.disablesCertificateValidation() } - - /** Gets a method call on this class that returns an instance of the class. */ - private DataFlow::CallNode getAChainedCall() { - result.(FindCall).getModelClass() = this - or - result.(CreateCall).getModelClass() = this - or - result.(CustomHttpCall).getModelClass() = this - or - result.(CollectionCall).getCollection().getModelClass() = this and - result.getMethodName() = ["first", "last"] - } - - /** Gets an API node referring to an instance of this class. */ - API::Node getAnInstanceReference() { - result = this.trackInstance() - or - result = this.getAChainedCall().track() - } - } - - /** DEPRECATED. Use `ModelClassNode` instead. */ - deprecated class ModelClass extends ClassDeclaration { - private ModelClassNode cls; - - ModelClass() { this = cls.getADeclaration() } - - /** Gets the class for which this is a declaration. */ - ModelClassNode getClassNode() { result = cls } - - /** Gets the API node for this class object. */ - deprecated API::Node getModelApiNode() { result = cls.trackModule() } - - /** Gets a call to `site=`, which sets the base URL for this model. */ - SiteAssignCall getASiteAssignment() { result = cls.getASiteAssignment() } - - /** Holds if `c` sets a base URL which does not use HTTPS. */ - predicate disablesCertificateValidation(SiteAssignCall c) { - cls.disablesCertificateValidation(c) - } } /** @@ -98,20 +62,25 @@ module ActiveResource { * ``` */ class ModelClassMethodCall extends DataFlow::CallNode { - private ModelClassNode cls; + API::Node model; - ModelClassMethodCall() { this = cls.trackModule().getAMethodCall(_) } + ModelClassMethodCall() { + model = modelApiNode() and + this = classMethodCall(model, _) + } /** Gets the model class for this call. */ - ModelClassNode getModelClass() { result = cls } + ModelClass getModelClass() { result.getModelApiNode() = model } } /** * A call to `site=` on an ActiveResource model class. * This sets the base URL for all HTTP requests made by this class. */ - private class SiteAssignCall extends ModelClassMethodCall { - SiteAssignCall() { this.getMethodName() = "site=" } + private class SiteAssignCall extends DataFlow::CallNode { + API::Node model; + + SiteAssignCall() { model = modelApiNode() and this = classMethodCall(model, "site=") } /** * Gets a node that contributes to the URLs used for HTTP requests by the parent @@ -119,10 +88,12 @@ module ActiveResource { */ DataFlow::Node getAUrlPart() { result = this.getArgument(0) } + /** Gets the model class for this call. */ + ModelClass getModelClass() { result.getModelApiNode() = model } + /** Holds if this site value specifies HTTP rather than HTTPS. */ predicate disablesCertificateValidation() { this.getAUrlPart() - // TODO: We should not need all this just to get the string value .asExpr() .(ExprNodes::AssignExprCfgNode) .getRhs() @@ -170,70 +141,87 @@ module ActiveResource { } /** - * DEPRECATED. Use `ModelClassNode.getAnInstanceReference()` instead. - * * An ActiveResource model object. */ - deprecated class ModelInstance extends DataFlow::Node { - private ModelClassNode cls; + class ModelInstance extends DataFlow::Node { + ModelClass cls; - ModelInstance() { this = cls.getAnInstanceReference().getAValueReachableFromSource() } + ModelInstance() { + exists(API::Node model | model = modelApiNode() | + this = model.getInstance().getAValueReachableFromSource() and + cls.getModelApiNode() = model + ) + or + exists(FindCall call | call.flowsTo(this) | cls = call.getModelClass()) + or + exists(CreateCall call | call.flowsTo(this) | cls = call.getModelClass()) + or + exists(CustomHttpCall call | call.flowsTo(this) | cls = call.getModelClass()) + or + exists(CollectionCall call | + call.getMethodName() = ["first", "last"] and + call.flowsTo(this) + | + cls = call.getCollection().getModelClass() + ) + } /** Gets the model class for this instance. */ - ModelClassNode getModelClass() { result = cls } + ModelClass getModelClass() { result = cls } } /** * A call to a method on an ActiveResource model object. */ class ModelInstanceMethodCall extends DataFlow::CallNode { - private ModelClassNode cls; + ModelInstance i; - ModelInstanceMethodCall() { this = cls.getAnInstanceReference().getAMethodCall(_) } + ModelInstanceMethodCall() { this.getReceiver() = i } /** Gets the model instance for this call. */ - deprecated ModelInstance getInstance() { result = this.getReceiver() } + ModelInstance getInstance() { result = i } /** Gets the model class for this call. */ - ModelClassNode getModelClass() { result = cls } + ModelClass getModelClass() { result = i.getModelClass() } } /** - * DEPRECATED. Use `CollectionSource` instead. - * - * A data flow node that may refer to a collection of ActiveResource model objects. + * A collection of ActiveResource model objects. */ - deprecated class Collection extends DataFlow::Node { - Collection() { this = any(CollectionSource src).track().getAValueReachableFromSource() } - } + class Collection extends DataFlow::Node { + ModelClassMethodCall classMethodCall; - /** - * A call that returns a collection of ActiveResource model objects. - */ - class CollectionSource extends ModelClassMethodCall { - CollectionSource() { - this.getMethodName() = "all" - or - this.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") + Collection() { + classMethodCall.flowsTo(this) and + ( + classMethodCall.getMethodName() = "all" + or + classMethodCall.getMethodName() = "find" and + classMethodCall.getArgument(0).asExpr().getConstantValue().isStringlikeValue("all") + ) } + + /** Gets the model class for this collection. */ + ModelClass getModelClass() { result = classMethodCall.getModelClass() } } /** * A method call on a collection. */ class CollectionCall extends DataFlow::CallNode { - private CollectionSource collection; - - CollectionCall() { this = collection.track().getAMethodCall(_) } + CollectionCall() { this.getReceiver() instanceof Collection } /** Gets the collection for this call. */ - CollectionSource getCollection() { result = collection } + Collection getCollection() { result = this.getReceiver() } } private class ModelClassMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelClassMethodCall { + ModelClass cls; + ModelClassMethodCallAsHttpRequest() { + this.getModelClass() = cls and this.getMethodName() = ["all", "build", "create", "create!", "find", "first", "last"] } @@ -242,14 +230,12 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - this.getModelClass().disablesCertificateValidation(disablingNode) and + cls.disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { - result = this.getModelClass().getASiteAssignment().getAUrlPart() - } + override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } override DataFlow::Node getResponseBody() { result = this } } @@ -257,7 +243,10 @@ module ActiveResource { private class ModelInstanceMethodCallAsHttpRequest extends Http::Client::Request::Range, ModelInstanceMethodCall { + ModelClass cls; + ModelInstanceMethodCallAsHttpRequest() { + this.getModelClass() = cls and this.getMethodName() = [ "exists?", "reload", "save", "save!", "destroy", "delete", "get", "patch", "post", "put", @@ -270,15 +259,42 @@ module ActiveResource { override predicate disablesCertificateValidation( DataFlow::Node disablingNode, DataFlow::Node argumentOrigin ) { - this.getModelClass().disablesCertificateValidation(disablingNode) and + cls.disablesCertificateValidation(disablingNode) and // TODO: highlight real argument origin argumentOrigin = disablingNode } - override DataFlow::Node getAUrlPart() { - result = this.getModelClass().getASiteAssignment().getAUrlPart() - } + override DataFlow::Node getAUrlPart() { result = cls.getASiteAssignment().getAUrlPart() } override DataFlow::Node getResponseBody() { result = this } } + + /** + * A call to a class method. + * + * TODO: is this general enough to be useful elsewhere? + * + * Examples: + * ```rb + * class A + * def self.m; end + * + * m # call + * end + * + * A.m # call + * ``` + */ + private DataFlow::CallNode classMethodCall(API::Node classNode, string methodName) { + // A.m + result = classNode.getAMethodCall(methodName) + or + // class A + // A.m + // end + result.getReceiver().asExpr() instanceof ExprNodes::SelfVariableAccessCfgNode and + result.asExpr().getExpr().getEnclosingModule().(ClassDeclaration).getSuperclassExpr() = + classNode.getAValueReachableFromSource().asExpr().getExpr() and + result.getMethodName() = methodName + } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll index 98fbe241404..8e673c4255d 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/GraphQL.qll @@ -39,8 +39,13 @@ private API::Node graphQlSchema() { result = API::getTopLevelMember("GraphQL").g */ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { GraphqlRelayClassicMutationClass() { - this = - graphQlSchema().getMember("RelayClassicMutation").getADescendentModule().getADeclaration() + this.getSuperclassExpr() = + graphQlSchema() + .getMember("RelayClassicMutation") + .getASubclass() + .getAValueReachableFromSource() + .asExpr() + .getExpr() } } @@ -69,7 +74,13 @@ private class GraphqlRelayClassicMutationClass extends ClassDeclaration { */ private class GraphqlSchemaResolverClass extends ClassDeclaration { GraphqlSchemaResolverClass() { - this = graphQlSchema().getMember("Resolver").getADescendentModule().getADeclaration() + this.getSuperclassExpr() = + graphQlSchema() + .getMember("Resolver") + .getASubclass() + .getAValueReachableFromSource() + .asExpr() + .getExpr() } } @@ -92,7 +103,13 @@ private string getASupportedHttpMethod() { result = ["get", "post"] } */ class GraphqlSchemaObjectClass extends ClassDeclaration { GraphqlSchemaObjectClass() { - this = graphQlSchema().getMember("Object").getADescendentModule().getADeclaration() + this.getSuperclassExpr() = + graphQlSchema() + .getMember("Object") + .getASubclass() + .getAValueReachableFromSource() + .asExpr() + .getExpr() } /** Gets a `GraphqlFieldDefinitionMethodCall` called in this class. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll index dfb93a0e6e5..70744d6fcc8 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sqlite3.qll @@ -15,20 +15,21 @@ private import codeql.ruby.Concepts * https://github.com/sparklemotion/sqlite3-ruby */ module Sqlite3 { - private API::Node databaseConst() { - result = API::getTopLevelMember("SQLite3").getMember("Database") - } - - private API::Node dbInstance() { - result = databaseConst().getInstance() - or - // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } - result = databaseConst().getMethod("new").getBlock().getParameter(0) - } - /** Gets a method call with a receiver that is a database instance. */ private DataFlow::CallNode getADatabaseMethodCall(string methodName) { - result = dbInstance().getAMethodCall(methodName) + exists(API::Node dbInstance | + dbInstance = API::getTopLevelMember("SQLite3").getMember("Database").getInstance() and + ( + result = dbInstance.getAMethodCall(methodName) + or + // e.g. SQLite3::Database.new("foo.db") |db| { db.some_method } + exists(DataFlow::BlockNode block | + result.getMethodName() = methodName and + block = dbInstance.getAValueReachableFromSource().(DataFlow::CallNode).getBlock() and + block.getParameter(0).flowsTo(result.getReceiver()) + ) + ) + ) } /** A prepared but unexecuted SQL statement. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll index 7b8648bd2b1..73ef87f5fd5 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Twirp.qll @@ -16,28 +16,50 @@ module Twirp { /** * A Twirp service instantiation */ - deprecated class ServiceInstantiation extends DataFlow::CallNode { + class ServiceInstantiation extends DataFlow::CallNode { ServiceInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Service").getAnInstantiation() } /** - * Gets a handler's method. + * Gets a local source node for the Service instantiation argument (the service handler). */ - DataFlow::MethodNode getAHandlerMethodNode() { - result = this.getArgument(0).backtrack().getMethod(_).asCallable() + private DataFlow::LocalSourceNode getHandlerSource() { + result = this.getArgument(0).getALocalSource() } /** - * Gets a handler's method as an AST node. + * Gets the API::Node for the service handler's class. */ - Ast::Method getAHandlerMethod() { result = this.getAHandlerMethodNode().asCallableAstNode() } + private API::Node getAHandlerClassApiNode() { + result.getAnInstantiation() = this.getHandlerSource() + } + + /** + * Gets the AST module for the service handler's class. + */ + private Ast::Module getAHandlerClassAstNode() { + result = + this.getAHandlerClassApiNode() + .asSource() + .asExpr() + .(CfgNodes::ExprNodes::ConstantReadAccessCfgNode) + .getExpr() + .getModule() + } + + /** + * Gets a handler's method. + */ + Ast::Method getAHandlerMethod() { + result = this.getAHandlerClassAstNode().getAnInstanceMethod() + } } /** * A Twirp client */ - deprecated class ClientInstantiation extends DataFlow::CallNode { + class ClientInstantiation extends DataFlow::CallNode { ClientInstantiation() { this = API::getTopLevelMember("Twirp").getMember("Client").getAnInstantiation() } @@ -45,10 +67,7 @@ module Twirp { /** The URL of a Twirp service, considered as a sink. */ class ServiceUrlAsSsrfSink extends ServerSideRequestForgery::Sink { - ServiceUrlAsSsrfSink() { - this = - API::getTopLevelMember("Twirp").getMember("Client").getMethod("new").getArgument(0).asSink() - } + ServiceUrlAsSsrfSink() { exists(ClientInstantiation c | c.getArgument(0) = this) } } /** A parameter that will receive parts of the url when handling an incoming request. */ @@ -56,14 +75,7 @@ module Twirp { DataFlow::ParameterNode { UnmarshaledParameter() { - this = - API::getTopLevelMember("Twirp") - .getMember("Service") - .getMethod("new") - .getArgument(0) - .getMethod(_) - .getParameter(0) - .asSource() + exists(ServiceInstantiation i | i.getAHandlerMethod().getParameter(0) = this.asParameter()) } override string getSourceType() { result = "Twirp Unmarhaled Parameter" } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll index f7345b22ed1..4095beb10af 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Gem.qll @@ -24,7 +24,7 @@ module Gem { GemSpec() { this.getExtension() = "gemspec" and - specCall = API::getTopLevelMember("Gem").getMember("Specification").getMethod("new") and + specCall = API::root().getMember("Gem").getMember("Specification").getMethod("new") and specCall.getLocation().getFile() = this } @@ -42,7 +42,7 @@ module Gem { .getBlock() .getParameter(0) .getMethod(name + "=") - .getArgument(0) + .getParameter(0) .asSink() .asExpr() .getExpr() diff --git a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll index ad87ee37ecd..b445521adb8 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/core/Kernel.qll @@ -19,8 +19,7 @@ module Kernel { */ class KernelMethodCall extends DataFlow::CallNode { KernelMethodCall() { - // Match Kernel calls using local flow, to avoid finding singleton calls on subclasses - this = DataFlow::getConstant("Kernel").getAMethodCall(_) + this = API::getTopLevelMember("Kernel").getAMethodCall(_) or this.asExpr().getExpr() instanceof UnknownMethodCall and ( diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll index 21bc5f69dcb..2e3fa21a45b 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/ModelsAsData.qll @@ -44,7 +44,7 @@ private class SummarizedCallableFromModel extends SummarizedCallable { override Call getACall() { exists(API::MethodAccessNode base | ModelOutput::resolvedSummaryBase(type, path, base) and - result = base.asCall().asExpr().getExpr() + result = base.getCallNode().asExpr().getExpr() ) } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll index ed7a331c452..4c03522a9c5 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -99,10 +99,9 @@ API::Node getExtraNodeFromPath(string type, AccessPath path, int n) { // A row of form `any;Method[foo]` should match any method named `foo`. type = "any" and n = 1 and - exists(string methodName, DataFlow::CallNode call | - methodMatchedByName(path, methodName) and - call.getMethodName() = methodName and - result.(API::MethodAccessNode).asCall() = call + exists(EntryPointFromAnyType entry | + methodMatchedByName(path, entry.getName()) and + result = entry.getANode() ) } @@ -113,10 +112,20 @@ API::Node getExtraNodeFromType(string type) { constRef = getConstantFromConstPath(consts) | suffix = "!" and - result = constRef.track() + ( + result.(API::Node::Internal).asSourceInternal() = constRef + or + result.(API::Node::Internal).asSourceInternal() = + constRef.getADescendentModule().getAnOwnModuleSelf() + ) or suffix = "" and - result = constRef.track().getInstance() + ( + result.(API::Node::Internal).asSourceInternal() = constRef.getAMethodCall("new") + or + result.(API::Node::Internal).asSourceInternal() = + constRef.getADescendentModule().getAnInstanceSelf() + ) ) or type = "" and @@ -136,6 +145,21 @@ private predicate methodMatchedByName(AccessPath path, string methodName) { ) } +/** + * An API graph entry point corresponding to a method name such as `foo` in `;any;Method[foo]`. + * + * This ensures that the API graph rooted in that method call is materialized. + */ +private class EntryPointFromAnyType extends API::EntryPoint { + string name; + + EntryPointFromAnyType() { this = "AnyMethod[" + name + "]" and methodMatchedByName(_, name) } + + override DataFlow::CallNode getACall() { result.getMethodName() = name } + + string getName() { result = name } +} + /** * Gets a Ruby-specific API graph successor of `node` reachable by resolving `token`. */ @@ -151,11 +175,9 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { result = node.getInstance() or token.getName() = "Parameter" and - exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | - argPos = FlowSummaryImplSpecific::parseParamBody(token.getAnArgument()) and - DataFlowDispatch::parameterMatch(paramPos, argPos) and - result = node.getParameterAtPosition(paramPos) - ) + result = + node.getASuccessor(API::Label::getLabelFromParameterPosition(FlowSummaryImplSpecific::parseArgBody(token + .getAnArgument()))) or exists(DataFlow::ContentSet contents | SummaryComponent::content(contents) = FlowSummaryImplSpecific::interpretComponentSpecific(token) and @@ -169,11 +191,9 @@ API::Node getExtraSuccessorFromNode(API::Node node, AccessPathToken token) { bindingset[token] API::Node getExtraSuccessorFromInvoke(InvokeNode node, AccessPathToken token) { token.getName() = "Argument" and - exists(DataFlowDispatch::ArgumentPosition argPos, DataFlowDispatch::ParameterPosition paramPos | - paramPos = FlowSummaryImplSpecific::parseArgBody(token.getAnArgument()) and - DataFlowDispatch::parameterMatch(paramPos, argPos) and - result = node.getArgumentAtPosition(argPos) - ) + result = + node.getASuccessor(API::Label::getLabelFromArgumentPosition(FlowSummaryImplSpecific::parseParamBody(token + .getAnArgument()))) } /** @@ -191,7 +211,7 @@ predicate invocationMatchesExtraCallSiteFilter(InvokeNode invoke, AccessPathToke /** An API graph node representing a method call. */ class InvokeNode extends API::MethodAccessNode { /** Gets the number of arguments to the call. */ - int getNumArgument() { result = this.asCall().getNumberOfArguments() } + int getNumArgument() { result = this.getCallNode().getNumberOfArguments() } } /** Gets the `InvokeNode` corresponding to a specific invocation of `node`. */ diff --git a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll index 3653fb23cee..31c9055d7cc 100644 --- a/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll +++ b/ruby/ql/lib/codeql/ruby/security/KernelOpenQuery.qll @@ -55,8 +55,7 @@ class AmbiguousPathCall extends DataFlow::CallNode { } private predicate methodCallOnlyOnIO(DataFlow::CallNode node, string methodName) { - // Use local flow to find calls to 'IO' without subclasses - node = DataFlow::getConstant("IO").getAMethodCall(methodName) and + node = API::getTopLevelMember("IO").getAMethodCall(methodName) and not node = API::getTopLevelMember("File").getAMethodCall(methodName) // needed in e.g. opal/opal, where some calls have both paths (opal implements an own corelib) } diff --git a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll index 637f7dab04a..261a7af462f 100644 --- a/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll +++ b/ruby/ql/lib/codeql/ruby/security/OpenSSL.qll @@ -597,7 +597,7 @@ private module Digest { call = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("new") | this = call.getReturn().getAMethodCall(["digest", "update", "<<"]) and - algo.matchesName(call.asCall() + algo.matchesName(call.getCallNode() .getArgument(0) .asExpr() .getExpr() @@ -619,7 +619,7 @@ private module Digest { Cryptography::HashingAlgorithm algo; DigestCallDirect() { - this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").asCall() and + this = API::getTopLevelMember("OpenSSL").getMember("Digest").getMethod("digest").getCallNode() and algo.matchesName(this.getArgument(0).asExpr().getExpr().getConstantValue().getString()) } diff --git a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll b/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll deleted file mode 100644 index 07b10cb1303..00000000000 --- a/ruby/ql/lib/codeql/ruby/typetracking/ApiGraphShared.qll +++ /dev/null @@ -1,328 +0,0 @@ -/** - * Parts of API graphs that can be shared with other dynamic languages. - * - * Depends on TypeTrackerSpecific for the corresponding language. - */ - -private import codeql.Locations -private import codeql.ruby.typetracking.TypeTracker -private import TypeTrackerSpecific - -/** - * The signature to use when instantiating `ApiGraphShared`. - * - * The implementor should define a newtype with at least three branches as follows: - * ```ql - * newtype TApiNode = - * MkForwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or - * MkBackwardNode(LocalSourceNode node, TypeTracker t) { isReachable(node, t) } or - * MkSinkNode(Node node) { ... } or - * ... - * ``` - * - * The three branches should be exposed through `getForwardNode`, `getBackwardNode`, and `getSinkNode`, respectively. - */ -signature module ApiGraphSharedSig { - /** A node in the API graph. */ - class ApiNode { - /** Gets a string representation of this API node. */ - string toString(); - - /** Gets the location associated with this API node, if any. */ - Location getLocation(); - } - - /** - * Gets the forward node with the given type-tracking state. - * - * This node will have outgoing epsilon edges to its type-tracking successors. - */ - ApiNode getForwardNode(TypeTrackingNode node, TypeTracker t); - - /** - * Gets the backward node with the given type-tracking state. - * - * This node will have outgoing epsilon edges to its type-tracking predecessors. - */ - ApiNode getBackwardNode(TypeTrackingNode node, TypeTracker t); - - /** - * Gets the sink node corresponding to `node`. - * - * Since sinks are not generally `LocalSourceNode`s, such nodes are materialised separately in order for - * the API graph to include representatives for sinks. Note that there is no corresponding case for "source" - * nodes as these are represented as forward nodes with initial-state type-trackers. - * - * Sink nodes have outgoing epsilon edges to the backward nodes corresponding to their local sources. - */ - ApiNode getSinkNode(Node node); - - /** - * Holds if a language-specific epsilon edge `pred -> succ` should be generated. - */ - predicate specificEpsilonEdge(ApiNode pred, ApiNode succ); -} - -/** - * Parts of API graphs that can be shared between language implementations. - */ -module ApiGraphShared { - private import S - - /** Gets a local source of `node`. */ - bindingset[node] - pragma[inline_late] - TypeTrackingNode getALocalSourceStrict(Node node) { result = node.getALocalSource() } - - cached - private module Cached { - /** - * Holds if there is an epsilon edge `pred -> succ`. - * - * That relation is reflexive, so `fastTC` produces the equivalent of a reflexive, transitive closure. - */ - pragma[noopt] - cached - predicate epsilonEdge(ApiNode pred, ApiNode succ) { - exists( - StepSummary summary, TypeTrackingNode predNode, TypeTracker predState, - TypeTrackingNode succNode, TypeTracker succState - | - StepSummary::stepCall(predNode, succNode, summary) - or - StepSummary::stepNoCall(predNode, succNode, summary) - | - pred = getForwardNode(predNode, predState) and - succState = StepSummary::append(predState, summary) and - succ = getForwardNode(succNode, succState) - or - succ = getBackwardNode(predNode, predState) and // swap order for backward flow - succState = StepSummary::append(predState, summary) and - pred = getBackwardNode(succNode, succState) // swap order for backward flow - ) - or - exists(Node sink, TypeTrackingNode localSource | - pred = getSinkNode(sink) and - localSource = getALocalSourceStrict(sink) and - succ = getBackwardStartNode(localSource) - ) - or - specificEpsilonEdge(pred, succ) - or - succ instanceof ApiNode and - succ = pred - } - - /** - * Holds if `pred` can reach `succ` by zero or more epsilon edges. - */ - cached - predicate epsilonStar(ApiNode pred, ApiNode succ) = fastTC(epsilonEdge/2)(pred, succ) - - /** Gets the API node to use when starting forward flow from `source` */ - cached - ApiNode forwardStartNode(TypeTrackingNode source) { - result = getForwardNode(source, TypeTracker::end(false)) - } - - /** Gets the API node to use when starting backward flow from `sink` */ - cached - ApiNode backwardStartNode(TypeTrackingNode sink) { - // There is backward flow A->B iff there is forward flow B->A. - // The starting point of backward flow corresponds to the end of a forward flow, and vice versa. - result = getBackwardNode(sink, TypeTracker::end(_)) - } - - /** Gets `node` as a data flow source. */ - cached - TypeTrackingNode asSourceCached(ApiNode node) { node = forwardEndNode(result) } - - /** Gets `node` as a data flow sink. */ - cached - Node asSinkCached(ApiNode node) { node = getSinkNode(result) } - } - - private import Cached - - /** Gets an API node corresponding to the end of forward-tracking to `localSource`. */ - pragma[nomagic] - private ApiNode forwardEndNode(TypeTrackingNode localSource) { - result = getForwardNode(localSource, TypeTracker::end(_)) - } - - /** Gets an API node corresponding to the end of backtracking to `localSource`. */ - pragma[nomagic] - private ApiNode backwardEndNode(TypeTrackingNode localSource) { - result = getBackwardNode(localSource, TypeTracker::end(false)) - } - - /** Gets a node reachable from `node` by zero or more epsilon edges, including `node` itself. */ - bindingset[node] - pragma[inline_late] - ApiNode getAnEpsilonSuccessorInline(ApiNode node) { epsilonStar(node, result) } - - /** Gets `node` as a data flow sink. */ - bindingset[node] - pragma[inline_late] - Node asSinkInline(ApiNode node) { result = asSinkCached(node) } - - /** Gets `node` as a data flow source. */ - bindingset[node] - pragma[inline_late] - TypeTrackingNode asSourceInline(ApiNode node) { result = asSourceCached(node) } - - /** Gets a value reachable from `source`. */ - bindingset[source] - pragma[inline_late] - Node getAValueReachableFromSourceInline(ApiNode source) { - exists(TypeTrackingNode src | - src = asSourceInline(getAnEpsilonSuccessorInline(source)) and - src.flowsTo(pragma[only_bind_into](result)) - ) - } - - /** Gets a value that can reach `sink`. */ - bindingset[sink] - pragma[inline_late] - Node getAValueReachingSinkInline(ApiNode sink) { - result = asSinkInline(getAnEpsilonSuccessorInline(sink)) - } - - /** - * Gets the starting point for forward-tracking at `node`. - * - * Should be used to obtain the successor of an edge when constructing labelled edges. - */ - bindingset[node] - pragma[inline_late] - ApiNode getForwardStartNode(Node node) { result = forwardStartNode(node) } - - /** - * Gets the starting point of backtracking from `node`. - * - * Should be used to obtain the successor of an edge when constructing labelled edges. - */ - bindingset[node] - pragma[inline_late] - ApiNode getBackwardStartNode(Node node) { result = backwardStartNode(node) } - - /** - * Gets a possible ending point of forward-tracking at `node`. - * - * Should be used to obtain the predecessor of an edge when constructing labelled edges. - * - * This is not backed by a `cached` predicate, and should only be used for materialising `cached` - * predicates in the API graph implementation - it should not be called in later stages. - */ - bindingset[node] - pragma[inline_late] - ApiNode getForwardEndNode(Node node) { result = forwardEndNode(node) } - - /** - * Gets a possible ending point backtracking to `node`. - * - * Should be used to obtain the predecessor of an edge when constructing labelled edges. - * - * This is not backed by a `cached` predicate, and should only be used for materialising `cached` - * predicates in the API graph implementation - it should not be called in later stages. - */ - bindingset[node] - pragma[inline_late] - ApiNode getBackwardEndNode(Node node) { result = backwardEndNode(node) } - - /** - * Gets a possible eding point of forward or backward tracking at `node`. - * - * Should be used to obtain the predecessor of an edge generated from store or load edges. - */ - bindingset[node] - pragma[inline_late] - ApiNode getForwardOrBackwardEndNode(Node node) { - result = getForwardEndNode(node) or result = getBackwardEndNode(node) - } - - /** Gets an API node for tracking forward starting at `node`. This is the implementation of `DataFlow::LocalSourceNode.track()` */ - bindingset[node] - pragma[inline_late] - ApiNode getNodeForForwardTracking(Node node) { result = forwardStartNode(node) } - - /** Gets an API node for backtracking starting at `node`. The implementation of `DataFlow::Node.backtrack()`. */ - bindingset[node] - pragma[inline_late] - ApiNode getNodeForBacktracking(Node node) { - result = getBackwardStartNode(getALocalSourceStrict(node)) - } - - /** Parts of the shared module to be re-exported by the user-facing `API` module. */ - module Public { - /** - * The signature to use when instantiating the `ExplainFlow` module. - */ - signature module ExplainFlowSig { - /** Holds if `node` should be a source. */ - predicate isSource(ApiNode node); - - /** Holds if `node` should be a sink. */ - default predicate isSink(ApiNode node) { any() } - - /** Holds if `node` should be skipped in the generated paths. */ - default predicate isHidden(ApiNode node) { none() } - } - - /** - * Module to help debug and visualize the data flows underlying API graphs. - * - * This module exports the query predicates for a path-problem query, and should be imported - * into the top-level of such a query. - * - * The module argument should specify source and sink API nodes, and the resulting query - * will show paths of epsilon edges that go from a source to a sink. Only epsilon edges are visualized. - * - * To condense the output a bit, paths in which the source and sink are the same node are omitted. - */ - module ExplainFlow { - private import T - - private ApiNode relevantNode() { - isSink(result) and - result = getAnEpsilonSuccessorInline(any(ApiNode node | isSource(node))) - or - epsilonEdge(result, relevantNode()) - } - - /** Holds if `node` is part of the graph to visualize. */ - query predicate nodes(ApiNode node) { node = relevantNode() and not isHidden(node) } - - private predicate edgeToHiddenNode(ApiNode pred, ApiNode succ) { - epsilonEdge(pred, succ) and - isHidden(succ) and - pred = relevantNode() and - succ = relevantNode() - } - - /** Holds if `pred -> succ` is an edge in the graph to visualize. */ - query predicate edges(ApiNode pred, ApiNode succ) { - nodes(pred) and - nodes(succ) and - exists(ApiNode mid | - edgeToHiddenNode*(pred, mid) and - epsilonEdge(mid, succ) - ) - } - - /** Holds for each source/sink pair to visualize in the graph. */ - query predicate problems( - ApiNode location, ApiNode sourceNode, ApiNode sinkNode, string message - ) { - nodes(sourceNode) and - nodes(sinkNode) and - isSource(sourceNode) and - isSink(sinkNode) and - sinkNode = getAnEpsilonSuccessorInline(sourceNode) and - sourceNode != sinkNode and - location = sinkNode and - message = "Node flows here" - } - } - } -} diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll index 001375b4dc5..fb3d7bf828f 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll @@ -55,9 +55,10 @@ private module Cached { ) } - /** Gets a type tracker with no content and the call bit set to the given value. */ - cached - TypeTracker noContentTypeTracker(boolean hasCall) { result = MkTypeTracker(hasCall, noContent()) } + pragma[nomagic] + private TypeTracker noContentTypeTracker(boolean hasCall) { + result = MkTypeTracker(hasCall, noContent()) + } /** Gets the summary resulting from appending `step` to type-tracking summary `tt`. */ cached @@ -317,8 +318,6 @@ class StepSummary extends TStepSummary { /** Provides predicates for updating step summaries (`StepSummary`s). */ module StepSummary { - predicate append = Cached::append/2; - /** * Gets the summary that corresponds to having taken a forwards * inter-procedural step from `nodeFrom` to `nodeTo`. @@ -379,35 +378,6 @@ module StepSummary { } deprecated predicate localSourceStoreStep = flowsToStoreStep/3; - - /** Gets the step summary for a level step. */ - StepSummary levelStep() { result = LevelStep() } - - /** Gets the step summary for a call step. */ - StepSummary callStep() { result = CallStep() } - - /** Gets the step summary for a return step. */ - StepSummary returnStep() { result = ReturnStep() } - - /** Gets the step summary for storing into `content`. */ - StepSummary storeStep(TypeTrackerContent content) { result = StoreStep(content) } - - /** Gets the step summary for loading from `content`. */ - StepSummary loadStep(TypeTrackerContent content) { result = LoadStep(content) } - - /** Gets the step summary for loading from `load` and then storing into `store`. */ - StepSummary loadStoreStep(TypeTrackerContent load, TypeTrackerContent store) { - result = LoadStoreStep(load, store) - } - - /** Gets the step summary for a step that only permits contents matched by `filter`. */ - StepSummary withContent(ContentFilter filter) { result = WithContent(filter) } - - /** Gets the step summary for a step that blocks contents matched by `filter`. */ - StepSummary withoutContent(ContentFilter filter) { result = WithoutContent(filter) } - - /** Gets the step summary for a jump step. */ - StepSummary jumpStep() { result = JumpStep() } } /** @@ -570,13 +540,6 @@ module TypeTracker { * Gets a valid end point of type tracking. */ TypeTracker end() { result.end() } - - /** - * INTERNAL USE ONLY. - * - * Gets a valid end point of type tracking with the call bit set to the given value. - */ - predicate end = Cached::noContentTypeTracker/1; } pragma[nomagic] diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected index 5677b16fedb..43b6490b052 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/ApiGraphs.expected @@ -1,8 +1,8 @@ classMethodCalls -| test1.rb:58:1:58:8 | ForwardNode(call to m) | -| test1.rb:59:1:59:8 | ForwardNode(call to m) | +| test1.rb:58:1:58:8 | Use getMember("M1").getMember("C1").getMethod("m").getReturn() | +| test1.rb:59:1:59:8 | Use getMember("M2").getMember("C3").getMethod("m").getReturn() | instanceMethodCalls -| test1.rb:61:1:61:12 | ForwardNode(call to m) | -| test1.rb:62:1:62:12 | ForwardNode(call to m) | +| test1.rb:61:1:61:12 | Use getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() | +| test1.rb:62:1:62:12 | Use getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() | flowThroughArray | test1.rb:73:1:73:10 | call to m | diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql deleted file mode 100644 index 93b5aaf745e..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.ql +++ /dev/null @@ -1,77 +0,0 @@ -import ruby -import codeql.ruby.ast.internal.TreeSitter -import codeql.ruby.dataflow.internal.AccessPathSyntax -import codeql.ruby.frameworks.data.internal.ApiGraphModels -import codeql.ruby.ApiGraphs -import TestUtilities.InlineExpectationsTest - -class AccessPathFromExpectation extends AccessPath::Range { - AccessPathFromExpectation() { hasExpectationWithValue(_, this) } -} - -API::Node evaluatePath(AccessPath path, int n) { - path instanceof AccessPathFromExpectation and - n = 1 and - exists(AccessPathToken token | token = path.getToken(0) | - token.getName() = "Member" and - result = API::getTopLevelMember(token.getAnArgument()) - or - token.getName() = "Method" and - result = API::getTopLevelCall(token.getAnArgument()) - or - token.getName() = "EntryPoint" and - result = token.getAnArgument().(API::EntryPoint).getANode() - ) - or - result = getSuccessorFromNode(evaluatePath(path, n - 1), path.getToken(n - 1)) - or - result = getSuccessorFromInvoke(evaluatePath(path, n - 1), path.getToken(n - 1)) - or - // TODO this is a workaround, support parsing of Method['[]'] instead - path.getToken(n - 1).getName() = "MethodBracket" and - result = evaluatePath(path, n - 1).getMethod("[]") -} - -API::Node evaluatePath(AccessPath path) { result = evaluatePath(path, path.getNumToken()) } - -module ApiUseTest implements TestSig { - string getARelevantTag() { result = ["source", "sink", "call", "reachableFromSource"] } - - predicate hasActualResult(Location location, string element, string tag, string value) { - // All results are considered optional - none() - } - - predicate hasOptionalResult(Location location, string element, string tag, string value) { - exists(API::Node apiNode, DataFlow::Node dataflowNode | - apiNode = evaluatePath(value) and - ( - tag = "source" and dataflowNode = apiNode.asSource() - or - tag = "reachableFromSource" and dataflowNode = apiNode.getAValueReachableFromSource() - or - tag = "sink" and dataflowNode = apiNode.asSink() - or - tag = "call" and dataflowNode = apiNode.asCall() - ) and - location = dataflowNode.getLocation() and - element = dataflowNode.toString() - ) - } -} - -import MakeTest - -class CustomEntryPointCall extends API::EntryPoint { - CustomEntryPointCall() { this = "CustomEntryPointCall" } - - override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } -} - -class CustomEntryPointUse extends API::EntryPoint { - CustomEntryPointUse() { this = "CustomEntryPointUse" } - - override DataFlow::LocalSourceNode getASource() { - result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" - } -} diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb index 35cf4471b48..34c4d17d212 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/callbacks.rb @@ -1,39 +1,39 @@ -Something.foo.withCallback do |a, b| #$ source=Member[Something].Method[foo].ReturnValue - a.something #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[0].Method[something].ReturnValue - b.somethingElse #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].Argument[block].Argument[1].Method[somethingElse].ReturnValue -end #$ source=Member[Something].Method[foo].ReturnValue.Method[withCallback].ReturnValue +Something.foo.withCallback do |a, b| #$ use=getMember("Something").getMethod("foo").getReturn() + a.something #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(0).getMethod("something").getReturn() + b.somethingElse #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getBlock().getParameter(1).getMethod("somethingElse").getReturn() +end #$ use=getMember("Something").getMethod("foo").getReturn().getMethod("withCallback").getReturn() -Something.withNamedArg do |a:, b: nil| #$ source=Member[Something] - a.something #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[a:].Method[something].ReturnValue - b.somethingElse #$ source=Member[Something].Method[withNamedArg].Argument[block].Parameter[b:].Method[somethingElse].ReturnValue -end #$ source=Member[Something].Method[withNamedArg].ReturnValue +Something.withNamedArg do |a:, b: nil| #$ use=getMember("Something") + a.something #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("a").getMethod("something").getReturn() + b.somethingElse #$ use=getMember("Something").getMethod("withNamedArg").getBlock().getKeywordParameter("b").getMethod("somethingElse").getReturn() +end #$ use=getMember("Something").getMethod("withNamedArg").getReturn() -Something.withLambda ->(a, b) { #$ source=Member[Something] - a.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[0].Method[something].ReturnValue - b.something #$ source=Member[Something].Method[withLambda].Argument[0].Parameter[1].Method[something].ReturnValue -} #$ source=Member[Something].Method[withLambda].ReturnValue +Something.withLambda ->(a, b) { #$ use=getMember("Something") + a.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(0).getMethod("something").getReturn() + b.something #$ use=getMember("Something").getMethod("withLambda").getParameter(0).getParameter(1).getMethod("something").getReturn() +} #$ use=getMember("Something").getMethod("withLambda").getReturn() -Something.namedCallback( #$ source=Member[Something] +Something.namedCallback( #$ use=getMember("Something") onEvent: ->(a, b) { - a.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[0].Method[something].ReturnValue - b.something #$ source=Member[Something].Method[namedCallback].Argument[onEvent:].Parameter[1].Method[something].ReturnValue + a.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(0).getMethod("something").getReturn() + b.something #$ use=getMember("Something").getMethod("namedCallback").getKeywordParameter("onEvent").getParameter(1).getMethod("something").getReturn() } -) #$ source=Member[Something].Method[namedCallback].ReturnValue +) #$ use=getMember("Something").getMethod("namedCallback").getReturn() -Something.nestedCall1 do |a| #$ source=Member[Something] - a.nestedCall2 do |b:| #$ reachableFromSource=Member[Something].Method[nestedCall1].Argument[block].Parameter[0] - b.something #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].Argument[block].Parameter[b:].Method[something].ReturnValue - end #$ source=Member[Something].Method[nestedCall1].Argument[block].Parameter[0].Method[nestedCall2].ReturnValue -end #$ source=Member[Something].Method[nestedCall1].ReturnValue +Something.nestedCall1 do |a| #$ use=getMember("Something") + a.nestedCall2 do |b:| #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0) + b.something #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getBlock().getKeywordParameter("b").getMethod("something").getReturn() + end #$ use=getMember("Something").getMethod("nestedCall1").getBlock().getParameter(0).getMethod("nestedCall2").getReturn() +end #$ use=getMember("Something").getMethod("nestedCall1").getReturn() def getCallback() ->(x) { - x.something #$ source=Member[Something].Method[indirectCallback].Argument[0].Parameter[0].Method[something].ReturnValue + x.something #$ use=getMember("Something").getMethod("indirectCallback").getParameter(0).getParameter(0).getMethod("something").getReturn() } end -Something.indirectCallback(getCallback()) #$ source=Member[Something].Method[indirectCallback].ReturnValue +Something.indirectCallback(getCallback()) #$ use=getMember("Something").getMethod("indirectCallback").getReturn() -Something.withMixed do |a, *args, b| #$ source=Member[Something] - a.something #$ source=Member[Something].Method[withMixed].Argument[block].Parameter[0].Method[something].ReturnValue +Something.withMixed do |a, *args, b| #$ use=getMember("Something") + a.something #$ use=getMember("Something").getMethod("withMixed").getBlock().getParameter(0).getMethod("something").getReturn() # b.something # not currently handled correctly -end #$ source=Member[Something].Method[withMixed].ReturnValue +end #$ use=getMember("Something").getMethod("withMixed").getReturn() diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb deleted file mode 100644 index b0e4f07e701..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/chained-access.rb +++ /dev/null @@ -1,31 +0,0 @@ -def chained_access1 - Something.foo [[[ - 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] - ]]] -end - -def chained_access2 - array = [] - array[0] = [[ - 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] - ]] - Something.foo array -end - -def chained_access3 - array = [[]] - array[0][0] = [ - 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[0].Element[0].Element[0] - ] - Something.foo array -end - -def chained_access4 - Something.foo { - :one => { - :two => { - :three => 'sink' # $ sink=Member[Something].Method[foo].Argument[0].Element[:one].Element[:two].Element[:three] - } - } - } -end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb deleted file mode 100644 index 63a4b4c3dce..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/method-callbacks.rb +++ /dev/null @@ -1,64 +0,0 @@ -class BaseClass - def inheritedInstanceMethod - yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[inheritedInstanceMethod].Parameter[block].Argument[0] - end - - def self.inheritedSingletonMethod - yield "taint" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[inheritedSingletonMethod].Parameter[block].Argument[0] - end -end - -class ClassWithCallbacks < BaseClass - def instanceMethod - yield "taint" # $ sink=Member[Something].Method[foo].Argument[block].ReturnValue.Method[instanceMethod].Parameter[block].Argument[0] - end - - def self.singletonMethod - yield "bar" # $ sink=Member[Something].Method[bar].Argument[block].ReturnValue.Method[singletonMethod].Parameter[block].Argument[0] - end - - def escapeSelf - Something.baz { self } - end - - def self.escapeSingletonSelf - Something.baz { self } - end - - def self.foo x - x # $ reachableFromSource=Member[BaseClass].Method[foo].Parameter[0] - x # $ reachableFromSource=Member[ClassWithCallbacks].Method[foo].Parameter[0] - x # $ reachableFromSource=Member[Subclass].Method[foo].Parameter[0] - end - - def bar x - x # $ reachableFromSource=Member[BaseClass].Instance.Method[bar].Parameter[0] - x # $ reachableFromSource=Member[ClassWithCallbacks].Instance.Method[bar].Parameter[0] - x # $ reachableFromSource=Member[Subclass].Instance.Method[bar].Parameter[0] - end -end - -class Subclass < ClassWithCallbacks - def instanceMethodInSubclass - yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[instanceMethodInSubclass].Parameter[block].Argument[0] - end - - def self.singletonMethodInSubclass - yield "bar" # $ sink=Member[Something].Method[baz].Argument[block].ReturnValue.Method[singletonMethodInSubclass].Parameter[block].Argument[0] - end -end - -Something.foo { ClassWithCallbacks.new } -Something.bar { ClassWithCallbacks } - -class ClassWithCallMethod - def call x - x # $ reachableFromSource=Method[topLevelMethod].Argument[0].Parameter[0] - "bar" # $ sink=Method[topLevelMethod].Argument[0].ReturnValue - end -end - -topLevelMethod ClassWithCallMethod.new - -blah = topLevelMethod -blah # $ reachableFromSource=Method[topLevelMethod].ReturnValue diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb deleted file mode 100644 index 178cacbe2c0..00000000000 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/self-dot-class.rb +++ /dev/null @@ -1,10 +0,0 @@ -module SelfDotClass - module Mixin - def foo - self.class.bar # $ call=Member[Foo].Method[bar] - end - end - class Subclass < Foo - include Mixin - end -end diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb index 3af793dd4f7..86b8bce9587 100644 --- a/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/test1.rb @@ -1,34 +1,34 @@ -MyModule #$ source=Member[MyModule] -print MyModule.foo #$ source=Member[MyModule].Method[foo].ReturnValue -Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue sink=Member[Kernel].Method[print].Argument[0] -Object::Kernel #$ source=Member[Kernel] -Object::Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue +MyModule #$ use=getMember("MyModule") +print MyModule.foo #$ use=getMember("MyModule").getMethod("foo").getReturn() +Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() def=getMember("Kernel").getMethod("print").getParameter(0) +Object::Kernel #$ use=getMember("Kernel") +Object::Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() begin - print MyModule.bar #$ source=Member[MyModule].Method[bar].ReturnValue - raise AttributeError #$ source=Member[AttributeError] -rescue AttributeError => e #$ source=Member[AttributeError] - Kernel.print(e) #$ source=Member[Kernel].Method[print].ReturnValue + print MyModule.bar #$ use=getMember("MyModule").getMethod("bar").getReturn() + raise AttributeError #$ use=getMember("AttributeError") +rescue AttributeError => e #$ use=getMember("AttributeError") + Kernel.print(e) #$ use=getMember("Kernel").getMethod("print").getReturn() end -Unknown.new.run #$ source=Member[Unknown].Method[new].ReturnValue.Method[run].ReturnValue -Foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] +Unknown.new.run #$ use=getMember("Unknown").getMethod("new").getReturn().getMethod("run").getReturn() +Foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") -Const = [1, 2, 3] #$ source=Member[Array].MethodBracket.ReturnValue -Const.each do |c| #$ source=Member[Const] - puts c #$ reachableFromSource=Member[Const].Method[each].Argument[block].Parameter[0] reachableFromSource=Member[Const].Element[any] -end #$ source=Member[Const].Method[each].ReturnValue sink=Member[Const].Method[each].Argument[block] +Const = [1, 2, 3] #$ use=getMember("Array").getMethod("[]").getReturn() +Const.each do |c| #$ use=getMember("Const") + puts c #$ use=getMember("Const").getMethod("each").getBlock().getParameter(0) use=getMember("Const").getContent(element) +end #$ use=getMember("Const").getMethod("each").getReturn() def=getMember("Const").getMethod("each").getBlock() -foo = Foo #$ source=Member[Foo] -foo::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] +foo = Foo #$ use=getMember("Foo") +foo::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") -FooAlias = Foo #$ source=Member[Foo] -FooAlias::Bar::Baz #$ source=Member[Foo].Member[Bar].Member[Baz] source=Member[FooAlias].Member[Bar].Member[Baz] +FooAlias = Foo #$ use=getMember("Foo") +FooAlias::Bar::Baz #$ use=getMember("Foo").getMember("Bar").getMember("Baz") module Outer module Inner end end -Outer::Inner.foo #$ source=Member[Outer].Member[Inner].Method[foo].ReturnValue +Outer::Inner.foo #$ use=getMember("Outer").getMember("Inner").getMethod("foo").getReturn() module M1 class C1 @@ -40,36 +40,36 @@ module M1 end end -class C2 < M1::C1 #$ source=Member[M1].Member[C1] +class C2 < M1::C1 #$ use=getMember("M1").getMember("C1") end module M2 - class C3 < M1::C1 #$ source=Member[M1].Member[C1] + class C3 < M1::C1 #$ use=getMember("M1").getMember("C1") end - class C4 < C2 #$ source=Member[C2] + class C4 < C2 #$ use=getMember("C2") end end -C2 #$ source=Member[C2] reachableFromSource=Member[M1].Member[C1] -M2::C3 #$ source=Member[M2].Member[C3] reachableFromSource=Member[M1].Member[C1] -M2::C4 #$ source=Member[M2].Member[C4] reachableFromSource=Member[C2] reachableFromSource=Member[M1].Member[C1] +C2 #$ use=getMember("C2") use=getMember("M1").getMember("C1").getASubclass() +M2::C3 #$ use=getMember("M2").getMember("C3") use=getMember("M1").getMember("C1").getASubclass() +M2::C4 #$ use=getMember("M2").getMember("C4") use=getMember("C2").getASubclass() use=getMember("M1").getMember("C1").getASubclass().getASubclass() -M1::C1.m #$ source=Member[M1].Member[C1].Method[m].ReturnValue -M2::C3.m #$ source=Member[M2].Member[C3].Method[m].ReturnValue source=Member[M1].Member[C1].Method[m].ReturnValue +M1::C1.m #$ use=getMember("M1").getMember("C1").getMethod("m").getReturn() +M2::C3.m #$ use=getMember("M2").getMember("C3").getMethod("m").getReturn() use=getMember("M1").getMember("C1").getASubclass().getMethod("m").getReturn() -M1::C1.new.m #$ source=Member[M1].Member[C1].Method[new].ReturnValue.Method[m].ReturnValue -M2::C3.new.m #$ source=Member[M2].Member[C3].Method[new].ReturnValue.Method[m].ReturnValue +M1::C1.new.m #$ use=getMember("M1").getMember("C1").getMethod("new").getReturn().getMethod("m").getReturn() +M2::C3.new.m #$ use=getMember("M2").getMember("C3").getMethod("new").getReturn().getMethod("m").getReturn() -Foo.foo(a,b:c) #$ source=Member[Foo].Method[foo].ReturnValue sink=Member[Foo].Method[foo].Argument[0] sink=Member[Foo].Method[foo].Argument[b:] +Foo.foo(a,b:c) #$ use=getMember("Foo").getMethod("foo").getReturn() def=getMember("Foo").getMethod("foo").getParameter(0) def=getMember("Foo").getMethod("foo").getKeywordParameter("b") def userDefinedFunction(x, y) x.noApiGraph(y) - x.customEntryPointCall(y) #$ call=EntryPoint[CustomEntryPointCall] source=EntryPoint[CustomEntryPointCall].ReturnValue sink=EntryPoint[CustomEntryPointCall].Parameter[0] - x.customEntryPointUse(y) #$ source=EntryPoint[CustomEntryPointUse] + x.customEntryPointCall(y) #$ call=entryPoint("CustomEntryPointCall") use=entryPoint("CustomEntryPointCall").getReturn() rhs=entryPoint("CustomEntryPointCall").getParameter(0) + x.customEntryPointUse(y) #$ use=entryPoint("CustomEntryPointUse") end -array = [A::B::C] #$ source=Member[Array].MethodBracket.ReturnValue -array[0].m #$ source=Member[A].Member[B].Member[C].Method[m].ReturnValue source=Member[Array].MethodBracket.ReturnValue.Element[0].Method[m].ReturnValue +array = [A::B::C] #$ use=getMember("Array").getMethod("[]").getReturn() +array[0].m #$ use=getMember("A").getMember("B").getMember("C").getMethod("m").getReturn() -A::B::C[0] #$ source=Member[A].Member[B].Member[C].Element[0] +A::B::C[0] #$ use=getMember("A").getMember("B").getMember("C").getContent(element_0) diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected b/ruby/ql/test/library-tests/dataflow/api-graphs/use.expected similarity index 100% rename from ruby/ql/test/library-tests/dataflow/api-graphs/VerifyApiGraphExpectations.expected rename to ruby/ql/test/library-tests/dataflow/api-graphs/use.expected diff --git a/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql new file mode 100644 index 00000000000..a0c11640ce0 --- /dev/null +++ b/ruby/ql/test/library-tests/dataflow/api-graphs/use.ql @@ -0,0 +1,88 @@ +import codeql.ruby.AST +import codeql.ruby.DataFlow +import TestUtilities.InlineExpectationsTest +import codeql.ruby.ApiGraphs + +class CustomEntryPointCall extends API::EntryPoint { + CustomEntryPointCall() { this = "CustomEntryPointCall" } + + override DataFlow::CallNode getACall() { result.getMethodName() = "customEntryPointCall" } +} + +class CustomEntryPointUse extends API::EntryPoint { + CustomEntryPointUse() { this = "CustomEntryPointUse" } + + override DataFlow::LocalSourceNode getASource() { + result.(DataFlow::CallNode).getMethodName() = "customEntryPointUse" + } +} + +module ApiUseTest implements TestSig { + string getARelevantTag() { result = ["use", "def", "call"] } + + private predicate relevantNode(API::Node a, DataFlow::Node n, Location l, string tag) { + l = n.getLocation() and + ( + tag = "use" and + n = a.getAValueReachableFromSource() + or + tag = "def" and + n = a.asSink() + or + tag = "call" and + n = a.(API::MethodAccessNode).getCallNode() + ) + } + + predicate hasActualResult(Location location, string element, string tag, string value) { + tag = "use" and // def tags are always optional + exists(DataFlow::Node n | relevantNode(_, n, location, tag) | + // Only report the longest path on this line: + value = + max(API::Node a2, Location l2, DataFlow::Node n2 | + relevantNode(a2, n2, l2, tag) and + l2.getFile() = location.getFile() and + l2.getEndLine() = location.getEndLine() + | + a2.getPath() + order by + size(n2.asExpr().getExpr()), a2.getPath().length() desc, a2.getPath() desc + ) and + element = n.toString() + ) + } + + // We also permit optional annotations for any other path on the line. + // This is used to test subclass paths, which typically have a shorter canonical path. + predicate hasOptionalResult(Location location, string element, string tag, string value) { + exists(API::Node a, DataFlow::Node n | relevantNode(a, n, location, tag) | + element = n.toString() and + value = getAPath(a, _) + ) + } +} + +import MakeTest + +private int size(AstNode n) { not n instanceof StmtSequence and result = count(n.getAChild*()) } + +/** + * Gets a path of the given `length` from the root to the given node. + * This is a copy of `API::getAPath()` without the restriction on path length, + * which would otherwise rule out paths involving `getASubclass()`. + */ +string getAPath(API::Node node, int length) { + node instanceof API::Root and + length = 0 and + result = "" + or + exists(API::Node pred, API::Label::ApiLabel lbl, string predpath | + pred.getASuccessor(lbl) = node and + predpath = getAPath(pred, length - 1) and + exists(string dot | if length = 1 then dot = "" else dot = "." | + result = predpath + dot + lbl and + // avoid producing strings longer than 1MB + result.length() < 1000 * 1000 + ) + ) +} diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected index 4f1b0c30920..66da43ab78b 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.expected @@ -1,8 +1,6 @@ sourceTest | hello_world_server.rb:8:13:8:15 | req | -| hello_world_server.rb:32:18:32:20 | req | ssrfSinkTest | hello_world_client.rb:6:47:6:75 | "http://localhost:8080/twirp" | serviceInstantiationTest | hello_world_server.rb:24:11:24:61 | call to new | -| hello_world_server.rb:38:1:38:57 | call to new | diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql index fee49cbb48c..4c0494f9100 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql +++ b/ruby/ql/test/library-tests/frameworks/Twirp/Twirp.ql @@ -5,4 +5,4 @@ query predicate sourceTest(Twirp::UnmarshaledParameter source) { any() } query predicate ssrfSinkTest(Twirp::ServiceUrlAsSsrfSink sink) { any() } -deprecated query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } +query predicate serviceInstantiationTest(Twirp::ServiceInstantiation si) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb index 7cd117a5843..1aa0b9aa8de 100644 --- a/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb +++ b/ruby/ql/test/library-tests/frameworks/Twirp/hello_world_server.rb @@ -5,7 +5,7 @@ require_relative 'hello_world/service_twirp.rb' class HelloWorldHandler # test: request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -13,7 +13,7 @@ end class FakeHelloWorldHandler # test: !request - def hello(req, env) + def hello(req, env) puts ">> Hello #{req.name}" {message: "Hello #{req.name}"} end @@ -21,18 +21,9 @@ end handler = HelloWorldHandler.new() # test: serviceInstantiation -service = Example::HelloWorld::HelloWorldService.new(handler) +service = Example::HelloWorld::HelloWorldService.new(handler) path_prefix = "/twirp/" + service.full_name server = WEBrick::HTTPServer.new(Port: 8080) server.mount path_prefix, Rack::Handler::WEBrick, service server.start - -class StaticHandler - def self.hello(req, env) - puts ">> Hello #{req.name}" - {message: "Hello #{req.name}"} - end -end - -Example::HelloWorld::HelloWorldService.new(StaticHandler) diff --git a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected index 4eacd48bd60..71327350941 100644 --- a/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected +++ b/ruby/ql/test/library-tests/frameworks/action_dispatch/ActionDispatch.expected @@ -55,12 +55,12 @@ underscore | LotsOfCapitalLetters | lots_of_capital_letters | | invalid | invalid | mimeTypeInstances -| mime_type.rb:2:6:2:28 | ForwardNode(call to fetch) | -| mime_type.rb:3:6:3:32 | ForwardNode(call to new) | -| mime_type.rb:4:6:4:35 | ForwardNode(call to lookup) | -| mime_type.rb:5:6:5:43 | ForwardNode(call to lookup_by_extension) | -| mime_type.rb:6:6:6:47 | ForwardNode(call to register) | -| mime_type.rb:7:6:7:64 | ForwardNode(call to register_alias) | +| mime_type.rb:2:6:2:28 | Use getMember("Mime").getContent(element_text/html) | +| mime_type.rb:3:6:3:32 | Use getMember("Mime").getMember("Type").getMethod("new").getReturn() | +| mime_type.rb:4:6:4:35 | Use getMember("Mime").getMember("Type").getMethod("lookup").getReturn() | +| mime_type.rb:5:6:5:43 | Use getMember("Mime").getMember("Type").getMethod("lookup_by_extension").getReturn() | +| mime_type.rb:6:6:6:47 | Use getMember("Mime").getMember("Type").getMethod("register").getReturn() | +| mime_type.rb:7:6:7:64 | Use getMember("Mime").getMember("Type").getMethod("register_alias").getReturn() | mimeTypeMatchRegExpInterpretations | mime_type.rb:11:11:11:19 | "foo/bar" | | mime_type.rb:12:7:12:15 | "foo/bar" | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected index a3ee4ebebf5..0374a54c0c1 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.expected @@ -10,7 +10,6 @@ activeRecordInstances | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:13:5:13:40 | call to find_by | | ActiveRecord.rb:13:5:13:46 | call to users | -| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:55:5:57:7 | if ... | | ActiveRecord.rb:55:43:56:40 | then ... | @@ -108,14 +107,12 @@ activeRecordSqlExecutionRanges | ActiveRecord.rb:19:16:19:24 | condition | | ActiveRecord.rb:28:30:28:44 | ...[...] | | ActiveRecord.rb:29:20:29:42 | "id = '#{...}'" | -| ActiveRecord.rb:30:21:30:45 | call to [] | | ActiveRecord.rb:30:22:30:44 | "id = '#{...}'" | | ActiveRecord.rb:31:16:31:21 | <<-SQL | | ActiveRecord.rb:34:20:34:47 | "user.id = '#{...}'" | | ActiveRecord.rb:46:20:46:32 | ... + ... | | ActiveRecord.rb:52:16:52:28 | "name #{...}" | | ActiveRecord.rb:56:20:56:39 | "username = #{...}" | -| ActiveRecord.rb:68:21:68:44 | ...[...] | | ActiveRecord.rb:106:27:106:76 | "this is an unsafe annotation:..." | activeRecordModelClassMethodCalls | ActiveRecord.rb:2:3:2:17 | call to has_many | @@ -130,6 +127,7 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:31:5:31:35 | call to where | | ActiveRecord.rb:34:5:34:14 | call to where | | ActiveRecord.rb:34:5:34:48 | call to not | +| ActiveRecord.rb:35:5:35:51 | call to authenticate | | ActiveRecord.rb:36:5:36:30 | call to find_by_name | | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | | ActiveRecord.rb:46:5:46:33 | call to delete_by | @@ -137,6 +135,7 @@ activeRecordModelClassMethodCalls | ActiveRecord.rb:56:7:56:40 | call to find_by | | ActiveRecord.rb:60:5:60:33 | call to find_by | | ActiveRecord.rb:62:5:62:34 | call to find | +| ActiveRecord.rb:68:5:68:45 | call to delete_by | | ActiveRecord.rb:72:5:72:24 | call to create | | ActiveRecord.rb:76:5:76:66 | call to create | | ActiveRecord.rb:80:5:80:68 | call to create | @@ -153,96 +152,6 @@ activeRecordModelClassMethodCalls | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | | associations.rb:16:3:16:18 | call to belongs_to | | associations.rb:19:11:19:20 | call to new | -| associations.rb:21:9:21:21 | call to posts | -| associations.rb:21:9:21:28 | call to create | -| associations.rb:23:12:23:25 | call to comments | -| associations.rb:23:12:23:32 | call to create | -| associations.rb:25:11:25:22 | call to author | -| associations.rb:27:9:27:21 | call to posts | -| associations.rb:27:9:27:28 | call to create | -| associations.rb:29:1:29:13 | call to posts | -| associations.rb:29:1:29:22 | ... << ... | -| associations.rb:31:1:31:12 | call to author= | -| associations.rb:35:1:35:14 | call to comments | -| associations.rb:35:1:35:21 | call to create | -| associations.rb:35:1:35:28 | call to create | -| associations.rb:37:1:37:13 | call to posts | -| associations.rb:37:1:37:20 | call to reload | -| associations.rb:37:1:37:27 | call to create | -| associations.rb:39:1:39:15 | call to build_tag | -| associations.rb:40:1:40:15 | call to build_tag | -| associations.rb:42:1:42:13 | call to posts | -| associations.rb:42:1:42:25 | call to push | -| associations.rb:43:1:43:13 | call to posts | -| associations.rb:43:1:43:27 | call to concat | -| associations.rb:44:1:44:13 | call to posts | -| associations.rb:44:1:44:19 | call to build | -| associations.rb:45:1:45:13 | call to posts | -| associations.rb:45:1:45:20 | call to create | -| associations.rb:46:1:46:13 | call to posts | -| associations.rb:46:1:46:21 | call to create! | -| associations.rb:47:1:47:13 | call to posts | -| associations.rb:47:1:47:20 | call to delete | -| associations.rb:48:1:48:13 | call to posts | -| associations.rb:48:1:48:24 | call to delete_all | -| associations.rb:49:1:49:13 | call to posts | -| associations.rb:49:1:49:21 | call to destroy | -| associations.rb:50:1:50:13 | call to posts | -| associations.rb:50:1:50:25 | call to destroy_all | -| associations.rb:51:1:51:13 | call to posts | -| associations.rb:51:1:51:22 | call to distinct | -| associations.rb:51:1:51:36 | call to find | -| associations.rb:52:1:52:13 | call to posts | -| associations.rb:52:1:52:19 | call to reset | -| associations.rb:52:1:52:33 | call to find | -| associations.rb:53:1:53:13 | call to posts | -| associations.rb:53:1:53:20 | call to reload | -| associations.rb:53:1:53:34 | call to find | -activeRecordModelClassMethodCallsReplacement -| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:2:3:2:17 | call to has_many | -| ActiveRecord.rb:1:1:3:3 | UserGroup | ActiveRecord.rb:13:5:13:40 | call to find_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:6:3:6:24 | call to belongs_to | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:9:5:9:68 | call to find | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:19:5:19:25 | call to destroy_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:28:5:28:45 | call to calculate | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:29:5:29:43 | call to delete_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:30:5:30:46 | call to destroy_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:31:5:31:35 | call to where | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:34:5:34:14 | call to where | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:35:5:35:51 | call to authenticate | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:36:5:36:30 | call to find_by_name | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:37:5:37:36 | call to not_a_find_by_method | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:46:5:46:33 | call to delete_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:52:5:52:29 | call to order | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:56:7:56:40 | call to find_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:60:5:60:33 | call to find_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:62:5:62:34 | call to find | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:68:5:68:45 | call to delete_by | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:72:5:72:24 | call to create | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:76:5:76:66 | call to create | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:80:5:80:68 | call to create | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:84:5:84:16 | call to create | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:88:5:88:27 | call to update | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:92:5:92:69 | call to update | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:96:5:96:71 | call to update | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:102:13:102:54 | call to annotate | -| ActiveRecord.rb:5:1:15:3 | User | ActiveRecord.rb:106:13:106:77 | call to annotate | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:19:5:19:25 | call to destroy_by | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:68:5:68:45 | call to delete_by | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:72:5:72:24 | call to create | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:76:5:76:66 | call to create | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:80:5:80:68 | call to create | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:84:5:84:16 | call to create | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:88:5:88:27 | call to update | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:92:5:92:69 | call to update | -| ActiveRecord.rb:17:1:21:3 | Admin | ActiveRecord.rb:96:5:96:71 | call to update | -| associations.rb:1:1:3:3 | Author | associations.rb:2:3:2:17 | call to has_many | -| associations.rb:1:1:3:3 | Author | associations.rb:19:11:19:20 | call to new | -| associations.rb:5:1:9:3 | Post | associations.rb:6:3:6:20 | call to belongs_to | -| associations.rb:5:1:9:3 | Post | associations.rb:7:3:7:20 | call to has_many | -| associations.rb:5:1:9:3 | Post | associations.rb:8:3:8:31 | call to has_and_belongs_to_many | -| associations.rb:11:1:13:3 | Tag | associations.rb:12:3:12:32 | call to has_and_belongs_to_many | -| associations.rb:15:1:17:3 | Comment | associations.rb:16:3:16:18 | call to belongs_to | potentiallyUnsafeSqlExecutingMethodCall | ActiveRecord.rb:9:5:9:68 | call to find | | ActiveRecord.rb:19:5:19:25 | call to destroy_by | diff --git a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql index 348ca1456e2..731679e437b 100644 --- a/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql +++ b/ruby/ql/test/library-tests/frameworks/active_record/ActiveRecord.ql @@ -9,19 +9,9 @@ query predicate activeRecordInstances(ActiveRecordInstance i) { any() } query predicate activeRecordSqlExecutionRanges(ActiveRecordSqlExecutionRange range) { any() } -deprecated query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { - any() -} +query predicate activeRecordModelClassMethodCalls(ActiveRecordModelClassMethodCall call) { any() } -query predicate activeRecordModelClassMethodCallsReplacement( - ActiveRecordModelClass cls, DataFlow::CallNode call -) { - call = cls.getClassNode().trackModule().getAMethodCall(_) -} - -deprecated query predicate potentiallyUnsafeSqlExecutingMethodCall( - PotentiallyUnsafeSqlExecutingMethodCall call -) { +query predicate potentiallyUnsafeSqlExecutingMethodCall(PotentiallyUnsafeSqlExecutingMethodCall call) { any() } diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected index e6d3b056971..a55946c1852 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.expected @@ -33,13 +33,6 @@ modelInstances | active_resource.rb:26:9:26:14 | people | | active_resource.rb:26:9:26:20 | call to first | | active_resource.rb:27:1:27:5 | alice | -modelInstancesAsSource -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:5:9:5:33 | call to new | -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:8:9:8:22 | call to find | -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:16:1:16:23 | call to new | -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:18:1:18:22 | call to get | -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:24:10:24:26 | call to find | -| active_resource.rb:1:1:3:3 | Person | active_resource.rb:26:9:26:20 | call to first | modelInstanceMethodCalls | active_resource.rb:6:1:6:10 | call to save | | active_resource.rb:9:1:9:13 | call to address= | @@ -57,6 +50,3 @@ collections | active_resource.rb:24:1:24:26 | ... = ... | | active_resource.rb:24:10:24:26 | call to find | | active_resource.rb:26:9:26:14 | people | -collectionSources -| active_resource.rb:23:10:23:19 | call to all | -| active_resource.rb:24:10:24:26 | call to find | diff --git a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql index f1898ddbc98..1f2fd1efcf1 100644 --- a/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql +++ b/ruby/ql/test/library-tests/frameworks/active_resource/ActiveResource.ql @@ -3,8 +3,7 @@ import codeql.ruby.DataFlow import codeql.ruby.frameworks.ActiveResource query predicate modelClasses( - ActiveResource::ModelClassNode c, DataFlow::Node siteAssignCall, - boolean disablesCertificateValidation + ActiveResource::ModelClass c, DataFlow::Node siteAssignCall, boolean disablesCertificateValidation ) { c.getASiteAssignment() = siteAssignCall and if c.disablesCertificateValidation(siteAssignCall) @@ -14,16 +13,8 @@ query predicate modelClasses( query predicate modelClassMethodCalls(ActiveResource::ModelClassMethodCall c) { any() } -deprecated query predicate modelInstances(ActiveResource::ModelInstance c) { any() } - -query predicate modelInstancesAsSource( - ActiveResource::ModelClassNode cls, DataFlow::LocalSourceNode node -) { - node = cls.getAnInstanceReference().asSource() -} +query predicate modelInstances(ActiveResource::ModelInstance c) { any() } query predicate modelInstanceMethodCalls(ActiveResource::ModelInstanceMethodCall c) { any() } -deprecated query predicate collections(ActiveResource::Collection c) { any() } - -query predicate collectionSources(ActiveResource::CollectionSource c) { any() } +query predicate collections(ActiveResource::Collection c) { any() } diff --git a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected index 460c7da3145..0eaf24029ef 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected +++ b/ruby/ql/test/query-tests/security/cwe-079/StoredXSS.expected @@ -3,6 +3,7 @@ edges | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | +| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:2:9:2:20 | call to display_text | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:5:9:5:21 | call to local_assigns [element :display_text] | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | app/views/foo/stores/show.html.erb:9:9:9:21 | call to local_assigns [element :display_text] | @@ -21,6 +22,7 @@ nodes | app/controllers/foo/stores_controller.rb:8:5:8:6 | dt | semmle.label | dt | | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | semmle.label | call to read | | app/controllers/foo/stores_controller.rb:9:22:9:23 | dt | semmle.label | dt | +| app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | semmle.label | call to raw_name | | app/controllers/foo/stores_controller.rb:13:55:13:56 | dt | semmle.label | dt | | app/views/foo/bars/_widget.html.erb:5:9:5:20 | call to display_text | semmle.label | call to display_text | | app/views/foo/bars/_widget.html.erb:8:9:8:21 | call to local_assigns [element :display_text] | semmle.label | call to local_assigns [element :display_text] | @@ -37,7 +39,11 @@ nodes | app/views/foo/stores/show.html.erb:40:64:40:87 | ... + ... | semmle.label | ... + ... | | app/views/foo/stores/show.html.erb:40:76:40:87 | call to display_text | semmle.label | call to display_text | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | semmle.label | call to handle | +| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | semmle.label | call to raw_name | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | semmle.label | call to handle | +| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | semmle.label | call to raw_name | +| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | semmle.label | call to display_name | +| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | semmle.label | @other_user_raw_name | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | semmle.label | call to sprintf | | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | semmle.label | call to handle | subpaths @@ -51,5 +57,9 @@ subpaths | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:32:3:32:14 | call to display_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | app/views/foo/stores/show.html.erb:37:3:37:16 | @instance_text | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:8:10:8:29 | call to read | stored value | | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:46:5:46:16 | call to handle | stored value | +| app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:49:5:49:18 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:63:3:63:18 | call to handle | stored value | +| app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:69:3:69:20 | call to raw_name | stored value | +| app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:79:5:79:22 | call to display_name | stored value | +| app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | app/views/foo/stores/show.html.erb:82:5:82:24 | @other_user_raw_name | Stored cross-site scripting vulnerability due to $@. | app/controllers/foo/stores_controller.rb:12:28:12:48 | call to raw_name | stored value | | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | app/views/foo/stores/show.html.erb:86:3:86:29 | call to sprintf | Stored cross-site scripting vulnerability due to $@. | app/views/foo/stores/show.html.erb:86:17:86:28 | call to handle | stored value | diff --git a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb index d8afec1c432..29656a15a3d 100644 --- a/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb +++ b/ruby/ql/test/query-tests/security/cwe-079/app/views/foo/stores/show.html.erb @@ -63,7 +63,7 @@ some_user.handle.html_safe %> -<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> +<%# BAD: Indirect to a database value without escaping %> <%= some_user = User.find 1 some_user.raw_name.html_safe @@ -75,10 +75,10 @@ some_user.handle %> -<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> +<%# BAD: Indirect to a database value without escaping %> <%= @user.display_name.html_safe %> -<%# BAD: Indirect to a database value without escaping (currently missed due to lack of 'self' handling in ORM tracking) %> +<%# BAD: Indirect to a database value without escaping %> <%= @other_user_raw_name.html_safe %> <%# BAD: Kernel.sprintf is a taint-step %> diff --git a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected index 161cdcc7751..0cc0d213dcc 100644 --- a/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-089/SqlInjection.expected @@ -22,7 +22,6 @@ edges | ActiveRecordInjection.rb:70:38:70:50 | ...[...] | ActiveRecordInjection.rb:8:31:8:34 | pass | | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | -| ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | @@ -36,7 +35,6 @@ edges | ActiveRecordInjection.rb:103:11:103:17 | ...[...] | ActiveRecordInjection.rb:103:5:103:7 | uid | | ActiveRecordInjection.rb:104:5:104:9 | uidEq | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | -| ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:20:22:20:30 | condition | | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | @@ -104,8 +102,6 @@ nodes | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | semmle.label | "id = '#{...}'" | | ActiveRecordInjection.rb:74:41:74:46 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:74:41:74:51 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:79:23:79:28 | call to params | semmle.label | call to params | -| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:83:17:83:22 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:84:19:84:24 | call to params | semmle.label | call to params | @@ -127,7 +123,6 @@ nodes | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | semmle.label | ... + ... | | ActiveRecordInjection.rb:141:21:141:26 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | -| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | semmle.label | ...[...] | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | semmle.label | "this is an unsafe annotation:..." | | ActiveRecordInjection.rb:155:59:155:64 | call to params | semmle.label | call to params | | ActiveRecordInjection.rb:155:59:155:74 | ...[...] | semmle.label | ...[...] | @@ -177,7 +172,6 @@ subpaths | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | ActiveRecordInjection.rb:62:21:62:26 | call to params | ActiveRecordInjection.rb:61:16:61:21 | <<-SQL | This SQL query depends on a $@. | ActiveRecordInjection.rb:62:21:62:26 | call to params | user-provided value | | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | ActiveRecordInjection.rb:68:34:68:39 | call to params | ActiveRecordInjection.rb:68:20:68:47 | "user.id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:68:34:68:39 | call to params | user-provided value | | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | ActiveRecordInjection.rb:74:41:74:46 | call to params | ActiveRecordInjection.rb:74:32:74:54 | "id = '#{...}'" | This SQL query depends on a $@. | ActiveRecordInjection.rb:74:41:74:46 | call to params | user-provided value | -| ActiveRecordInjection.rb:79:23:79:35 | ...[...] | ActiveRecordInjection.rb:79:23:79:28 | call to params | ActiveRecordInjection.rb:79:23:79:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:79:23:79:28 | call to params | user-provided value | | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | ActiveRecordInjection.rb:83:17:83:22 | call to params | ActiveRecordInjection.rb:83:17:83:31 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:83:17:83:22 | call to params | user-provided value | | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | ActiveRecordInjection.rb:84:19:84:24 | call to params | ActiveRecordInjection.rb:84:19:84:33 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:84:19:84:24 | call to params | user-provided value | | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | ActiveRecordInjection.rb:88:18:88:23 | call to params | ActiveRecordInjection.rb:88:18:88:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:88:18:88:23 | call to params | user-provided value | @@ -185,7 +179,6 @@ subpaths | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | ActiveRecordInjection.rb:94:18:94:23 | call to params | ActiveRecordInjection.rb:94:18:94:35 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:94:18:94:23 | call to params | user-provided value | | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | ActiveRecordInjection.rb:96:23:96:28 | call to params | ActiveRecordInjection.rb:96:23:96:47 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:96:23:96:28 | call to params | user-provided value | | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | ActiveRecordInjection.rb:102:10:102:15 | call to params | ActiveRecordInjection.rb:108:20:108:32 | ... + ... | This SQL query depends on a $@. | ActiveRecordInjection.rb:102:10:102:15 | call to params | user-provided value | -| ActiveRecordInjection.rb:141:21:141:44 | ...[...] | ActiveRecordInjection.rb:141:21:141:26 | call to params | ActiveRecordInjection.rb:141:21:141:44 | ...[...] | This SQL query depends on a $@. | ActiveRecordInjection.rb:141:21:141:26 | call to params | user-provided value | | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | ActiveRecordInjection.rb:155:59:155:64 | call to params | ActiveRecordInjection.rb:155:27:155:76 | "this is an unsafe annotation:..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:155:59:155:64 | call to params | user-provided value | | ActiveRecordInjection.rb:168:37:168:41 | query | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:168:37:168:41 | query | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | ActiveRecordInjection.rb:173:5:173:10 | call to params | ActiveRecordInjection.rb:177:43:177:104 | "SELECT * FROM users WHERE id ..." | This SQL query depends on a $@. | ActiveRecordInjection.rb:173:5:173:10 | call to params | user-provided value | @@ -196,4 +189,4 @@ subpaths | PgInjection.rb:20:22:20:25 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:20:22:20:25 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:21:28:21:31 | qry2 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:21:28:21:31 | qry2 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | | PgInjection.rb:32:29:32:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:32:29:32:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | -| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | +| PgInjection.rb:44:29:44:32 | qry3 | PgInjection.rb:6:12:6:17 | call to params | PgInjection.rb:44:29:44:32 | qry3 | This SQL query depends on a $@. | PgInjection.rb:6:12:6:17 | call to params | user-provided value | \ No newline at end of file diff --git a/shared/util/codeql/util/test/InlineExpectationsTest.qll b/shared/util/codeql/util/test/InlineExpectationsTest.qll index ec2f30c4119..83c6a851f7d 100644 --- a/shared/util/codeql/util/test/InlineExpectationsTest.qll +++ b/shared/util/codeql/util/test/InlineExpectationsTest.qll @@ -501,19 +501,6 @@ module Make { class FalseNegativeExpectation = LegacyTest::FalseNegativeTestExpectation; class InvalidExpectation = LegacyTest::InvalidTestExpectation; - - /** - * Holds if the expectation `tag=value` is found in one or more expectation comments. - * - * This can be used when writing tests where the set of possible values must be known in advance, - * for example, when testing a predicate for which `value` is part of the binding set. - */ - predicate hasExpectationWithValue(string tag, string value) { - exists(string tags | - getAnExpectation(_, _, _, tags, value) and - tag = tags.splitAt(",") - ) - } } /** From 90336705e1e8ebc1acc1b229a3842a98912fcad6 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 29 Jun 2023 14:48:17 +0100 Subject: [PATCH 88/93] C++: Accept test changes after merge. --- .../constant-size/ConstantSizeArrayOffByOne.expected | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) 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 3871d5b9e40..c4c821456f0 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 @@ -40,10 +40,7 @@ edges | test.cpp:159:25:159:29 | array | test.cpp:161:5:161:10 | access to array | | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | | test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array | -| test.cpp:198:14:198:20 | buffer1 | test.cpp:175:30:175:30 | p | -| test.cpp:198:14:198:20 | buffer1 | test.cpp:198:14:198:20 | buffer1 | -| test.cpp:201:14:201:20 | buffer2 | test.cpp:175:30:175:30 | p | -| test.cpp:201:14:201:20 | buffer2 | test.cpp:201:14:201:20 | buffer2 | +| test.cpp:175:30:175:30 | p | test.cpp:191:27:191:30 | access to array | | test.cpp:204:14:204:20 | buffer3 | test.cpp:175:30:175:30 | p | | test.cpp:204:14:204:20 | buffer3 | test.cpp:204:14:204:20 | buffer3 | | test.cpp:207:35:207:35 | p | test.cpp:208:14:208:14 | p | @@ -113,11 +110,8 @@ nodes | test.cpp:161:5:161:10 | access to array | semmle.label | access to array | | test.cpp:162:5:162:10 | access to array | semmle.label | access to array | | test.cpp:175:30:175:30 | p | semmle.label | p | +| test.cpp:175:30:175:30 | p | semmle.label | p | | test.cpp:191:27:191:30 | access to array | semmle.label | access to array | -| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 | -| test.cpp:198:14:198:20 | buffer1 | semmle.label | buffer1 | -| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 | -| test.cpp:201:14:201:20 | buffer2 | semmle.label | buffer2 | | test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | | test.cpp:204:14:204:20 | buffer3 | semmle.label | buffer3 | | test.cpp:207:35:207:35 | p | semmle.label | p | @@ -144,5 +138,4 @@ subpaths | 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 | | test.cpp:151:5:151:11 | PointerAdd: access to array | test.cpp:148:23:148:28 | buffer | test.cpp:151:5:151:11 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:147:19:147:24 | buffer | buffer | test.cpp:151:5:151:15 | Store: ... = ... | write | | test.cpp:162:5:162:10 | PointerAdd: access to array | test.cpp:159:25:159:29 | array | test.cpp:162:5:162:10 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:158:10:158:14 | array | array | test.cpp:162:5:162:19 | Store: ... = ... | write | -| test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:201:14:201:20 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:200:19:200:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read | | test.cpp:191:27:191:30 | PointerAdd: access to array | test.cpp:216:19:216:25 | buffer2 | test.cpp:191:27:191:30 | access to array | This pointer arithmetic may have an off-by-1 error allowing it to overrun $@ at this $@. | test.cpp:215:19:215:25 | buffer2 | buffer2 | test.cpp:191:27:191:30 | Load: access to array | read | From 59d9c6e3f2a3dee63ab325414ffe3ad3750ec4cc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 29 Jun 2023 16:50:57 +0100 Subject: [PATCH 89/93] C++: Use the pruning stage in the product-flow configuration. --- cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql b/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql index a9af2d08f51..1aa7551f63e 100644 --- a/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql +++ b/cpp/ql/src/Security/CWE/CWE-119/OverrunWriteProductFlow.ql @@ -120,6 +120,10 @@ module ValidState { predicate isBarrier(DataFlow::Node node, FlowState state) { none() } + predicate isBarrierOut(DataFlow::Node node) { + node = any(DataFlow::SsaPhiNode phi).getAnInput(true) + } + predicate isAdditionalFlowStep( DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2 ) { @@ -233,7 +237,8 @@ module StringSizeConfig implements ProductFlow::StateConfigSig { // we use `state2` to remember that there was an offset (in this case an offset of `1`) added // to the size of the allocation. This state is then checked in `isSinkPair`. exists(state1) and - hasSize(bufSource.asConvertedExpr(), sizeSource, state2) + hasSize(bufSource.asConvertedExpr(), sizeSource, state2) and + validState(sizeSource, state2) } predicate isSinkPair( From 97916407c2037564c921ed9762250ad784ef5052 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Thu, 29 Jun 2023 16:51:08 +0100 Subject: [PATCH 90/93] C++: Accept test changes. --- .../CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected | 6 ------ 1 file changed, 6 deletions(-) diff --git a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected index e1665c23315..a5e2ae3b0b6 100644 --- a/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected +++ b/cpp/ql/test/query-tests/Security/CWE/CWE-119/SAMATE/OverrunWriteProductFlow.expected @@ -75,7 +75,6 @@ edges | test.cpp:214:24:214:24 | p | test.cpp:216:10:216:10 | p | | test.cpp:220:43:220:48 | call to malloc | test.cpp:222:15:222:20 | buffer | | test.cpp:222:15:222:20 | buffer | test.cpp:214:24:214:24 | p | -| test.cpp:228:43:228:48 | call to malloc | test.cpp:232:10:232:15 | buffer | | test.cpp:235:40:235:45 | buffer | test.cpp:236:5:236:26 | ... = ... | | test.cpp:236:5:236:26 | ... = ... | test.cpp:236:12:236:17 | p_str indirection [post update] [string] | | test.cpp:241:27:241:32 | call to malloc | test.cpp:242:22:242:27 | buffer | @@ -86,7 +85,6 @@ edges | test.cpp:243:12:243:14 | str indirection [string] | test.cpp:243:16:243:21 | string indirection | | test.cpp:243:16:243:21 | string indirection | test.cpp:243:12:243:21 | string | | test.cpp:249:20:249:27 | call to my_alloc | test.cpp:250:12:250:12 | p | -| test.cpp:256:17:256:22 | call to malloc | test.cpp:257:12:257:12 | p | | test.cpp:262:22:262:27 | call to malloc | test.cpp:266:12:266:12 | p | | test.cpp:264:20:264:25 | call to malloc | test.cpp:266:12:266:12 | p | nodes @@ -155,8 +153,6 @@ nodes | test.cpp:216:10:216:10 | p | semmle.label | p | | test.cpp:220:43:220:48 | call to malloc | semmle.label | call to malloc | | test.cpp:222:15:222:20 | buffer | semmle.label | buffer | -| test.cpp:228:43:228:48 | call to malloc | semmle.label | call to malloc | -| test.cpp:232:10:232:15 | buffer | semmle.label | buffer | | test.cpp:235:40:235:45 | buffer | semmle.label | buffer | | test.cpp:236:5:236:26 | ... = ... | semmle.label | ... = ... | | test.cpp:236:12:236:17 | p_str indirection [post update] [string] | semmle.label | p_str indirection [post update] [string] | @@ -168,8 +164,6 @@ nodes | test.cpp:243:16:243:21 | string indirection | semmle.label | string indirection | | test.cpp:249:20:249:27 | call to my_alloc | semmle.label | call to my_alloc | | test.cpp:250:12:250:12 | p | semmle.label | p | -| test.cpp:256:17:256:22 | call to malloc | semmle.label | call to malloc | -| test.cpp:257:12:257:12 | p | semmle.label | p | | test.cpp:262:22:262:27 | call to malloc | semmle.label | call to malloc | | test.cpp:264:20:264:25 | call to malloc | semmle.label | call to malloc | | test.cpp:266:12:266:12 | p | semmle.label | p | From cf83baeead28fe5cb13fae12aafda4118d77ebe5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 00:17:47 +0000 Subject: [PATCH 91/93] Add changed framework coverage reports --- java/documentation/library-coverage/coverage.csv | 1 + java/documentation/library-coverage/coverage.rst | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/java/documentation/library-coverage/coverage.csv b/java/documentation/library-coverage/coverage.csv index 02d4c5aa4c8..bf73577e7cc 100644 --- a/java/documentation/library-coverage/coverage.csv +++ b/java/documentation/library-coverage/coverage.csv @@ -69,6 +69,7 @@ javax.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23 javax.management.remote,2,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,, javax.naming,7,,1,,,,,,,,,,,6,,1,,,,,,,,,,,,,,,,,,,,,,,1, javax.net.ssl,2,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +javax.portlet,,,61,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,61, javax.script,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,, javax.servlet,5,21,2,,,,,,,,1,,,,,,,,,1,,,,,,,,,,3,,,,,,,,21,2, javax.validation,1,1,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,, diff --git a/java/documentation/library-coverage/coverage.rst b/java/documentation/library-coverage/coverage.rst index 806100ff6d9..c8831af7a5b 100644 --- a/java/documentation/library-coverage/coverage.rst +++ b/java/documentation/library-coverage/coverage.rst @@ -19,9 +19,9 @@ Java framework & library support JBoss Logging,``org.jboss.logging``,,,324,,,,,, `JSON-java `_,``org.json``,,236,,,,,,, Java Standard Library,``java.*``,3,683,197,76,,9,,,17 - Java extensions,"``javax.*``, ``jakarta.*``",63,611,34,2,4,,1,1,2 + Java extensions,"``javax.*``, ``jakarta.*``",63,672,34,2,4,,1,1,2 Kotlin Standard Library,``kotlin*``,,1847,16,14,,,,,2 `Spring `_,``org.springframework.*``,29,483,115,4,,28,14,,35 Others,"``antlr``, ``cn.hutool.core.codec``, ``com.alibaba.druid.sql``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.google.gson``, ``com.hubspot.jinjava``, ``com.jcraft.jsch``, ``com.mitchellbosecke.pebble``, ``com.opensymphony.xwork2.ognl``, ``com.rabbitmq.client``, ``com.thoughtworks.xstream``, ``com.unboundid.ldap.sdk``, ``com.zaxxer.hikari``, ``flexjson``, ``freemarker.cache``, ``freemarker.template``, ``groovy.lang``, ``groovy.text``, ``groovy.util``, ``hudson``, ``io.jsonwebtoken``, ``io.netty.bootstrap``, ``io.netty.buffer``, ``io.netty.channel``, ``io.netty.handler.codec``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util``, ``javafx.scene.web``, ``jenkins``, ``jodd.json``, ``net.sf.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.acegisecurity``, ``org.antlr.runtime``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.exec``, ``org.apache.commons.httpclient.util``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.lang``, ``org.apache.commons.logging``, ``org.apache.commons.net``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.fs``, ``org.apache.hadoop.hive.metastore``, ``org.apache.hc.client5.http.async.methods``, ``org.apache.hc.client5.http.classic.methods``, ``org.apache.hc.client5.http.fluent``, ``org.apache.hive.hcatalog.templeton``, ``org.apache.ibatis.jdbc``, ``org.apache.log4j``, ``org.apache.shiro.codec``, ``org.apache.shiro.jndi``, ``org.apache.tools.ant``, ``org.apache.tools.zip``, ``org.apache.velocity.app``, ``org.apache.velocity.runtime``, ``org.codehaus.cargo.container.installer``, ``org.codehaus.groovy.control``, ``org.dom4j``, ``org.eclipse.jetty.client``, ``org.fusesource.leveldbjni``, ``org.geogebra.web.full.main``, ``org.hibernate``, ``org.influxdb``, ``org.jdbi.v3.core``, ``org.jenkins.ui.icon``, ``org.jenkins.ui.symbol``, ``org.jooq``, ``org.kohsuke.stapler``, ``org.mvel2``, ``org.openjdk.jmh.runner.options``, ``org.scijava.log``, ``org.slf4j``, ``org.thymeleaf``, ``org.xml.sax``, ``org.xmlpull.v1``, ``org.yaml.snakeyaml``, ``play.libs.ws``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",126,5232,577,89,6,18,18,,200 - Totals,,283,13532,2059,286,16,122,33,1,390 + Totals,,283,13593,2059,286,16,122,33,1,390 From a4d03371e54e1b2f443f6ed484fe3e0b5729ddc5 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Fri, 30 Jun 2023 11:10:15 +0100 Subject: [PATCH 92/93] Update cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp Co-authored-by: Jeroen Ketema <93738568+jketema@users.noreply.github.com> --- cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp index 347afddb4bd..c5004157af1 100644 --- a/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp +++ b/cpp/ql/test/library-tests/dataflow/dataflow-tests/test.cpp @@ -779,7 +779,7 @@ void test_sometimes_calls_sink_lt() { void sometimes_calls_sink_switch(int x, int n) { switch(n) { case 0: - sink(x); // $ ast,ir=790:31 SPURIOUS: ast,ir=788:31 // IR spurious results because IRGuard don't understand switch statements. + sink(x); // $ ast,ir=790:31 SPURIOUS: ast,ir=788:31 // IR spurious results because IRGuard doesn't understand switch statements. break; } } From d744b218f609f9390ef979198e9ad131374b7bbb Mon Sep 17 00:00:00 2001 From: Alex Denisov Date: Fri, 30 Jun 2023 13:05:40 +0200 Subject: [PATCH 93/93] Misc: add bazel buildifer pre-commit hook --- .pre-commit-config.yaml | 5 ++++ misc/bazel/workspace_deps.bzl | 4 +-- swift/codegen/BUILD.bazel | 10 +++---- swift/third_party/BUILD.binlog.bazel | 30 ++++++++++++------- .../BUILD.swift-llvm-support.bazel | 16 ++++++++-- swift/xcode-autobuilder/tests/BUILD.bazel | 27 ++++++++++------- 6 files changed, 61 insertions(+), 31 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2cf6f530354..1a2ed103df6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,6 +21,11 @@ repos: - id: autopep8 files: ^misc/codegen/.*\.py + - repo: https://github.com/warchant/pre-commit-buildifier + rev: 0.0.2 + hooks: + - id: buildifier + - repo: local hooks: - id: codeql-format diff --git a/misc/bazel/workspace_deps.bzl b/misc/bazel/workspace_deps.bzl index 674be778d78..ce91ee3959d 100644 --- a/misc/bazel/workspace_deps.bzl +++ b/misc/bazel/workspace_deps.bzl @@ -4,8 +4,8 @@ load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") def codeql_workspace_deps(repository_name = "codeql"): pip_install( - name = "codegen_deps", - requirements = "@%s//misc/codegen:requirements.txt" % repository_name, + name = "codegen_deps", + requirements = "@%s//misc/codegen:requirements.txt" % repository_name, ) bazel_skylib_workspace() rules_pkg_dependencies() diff --git a/swift/codegen/BUILD.bazel b/swift/codegen/BUILD.bazel index bc807e9329a..384d59b17e3 100644 --- a/swift/codegen/BUILD.bazel +++ b/swift/codegen/BUILD.bazel @@ -2,14 +2,14 @@ load("@bazel_skylib//rules:native_binary.bzl", "native_binary") native_binary( name = "codegen", - out = "codegen", src = "//misc/codegen", - data = [ - "//swift:schema", - "//swift:codegen_conf", - ], + out = "codegen", args = [ "--configuration-file=$(location //swift:codegen_conf)", ], + data = [ + "//swift:codegen_conf", + "//swift:schema", + ], visibility = ["//swift:__subpackages__"], ) diff --git a/swift/third_party/BUILD.binlog.bazel b/swift/third_party/BUILD.binlog.bazel index 71d42e45026..78db04fa3e2 100644 --- a/swift/third_party/BUILD.binlog.bazel +++ b/swift/third_party/BUILD.binlog.bazel @@ -1,19 +1,27 @@ cc_library( - name = "binlog", - hdrs = glob(["include/**/*.hpp"]), - srcs = glob(["include/**/*.cpp"]), - includes = ["include"], - visibility = ["//visibility:public"], + name = "binlog", + srcs = glob(["include/**/*.cpp"]), + hdrs = glob(["include/**/*.hpp"]), + includes = ["include"], + visibility = ["//visibility:public"], ) cc_binary( - name = "bread", - srcs = ["bin/bread.cpp", "bin/printers.hpp", "bin/printers.cpp", "bin/getopt.hpp"], - deps = [":binlog"], + name = "bread", + srcs = [ + "bin/bread.cpp", + "bin/getopt.hpp", + "bin/printers.cpp", + "bin/printers.hpp", + ], + deps = [":binlog"], ) cc_binary( - name = "brecovery", - srcs = ["bin/brecovery.cpp", "bin/getopt.hpp"], - deps = [":binlog"], + name = "brecovery", + srcs = [ + "bin/brecovery.cpp", + "bin/getopt.hpp", + ], + deps = [":binlog"], ) diff --git a/swift/third_party/BUILD.swift-llvm-support.bazel b/swift/third_party/BUILD.swift-llvm-support.bazel index 0a9068c4cf6..14530fcaac4 100644 --- a/swift/third_party/BUILD.swift-llvm-support.bazel +++ b/swift/third_party/BUILD.swift-llvm-support.bazel @@ -2,8 +2,19 @@ load("@rules_pkg//:mappings.bzl", "pkg_files", "strip_prefix") cc_library( name = "swift-llvm-support", - srcs = glob(["*.a", "*.so", "*.dylib"]), - hdrs = glob(["include/**/*", "stdlib/**/*" ]), + srcs = glob([ + "*.a", + "*.so", + "*.dylib", + ]), + hdrs = glob([ + "include/**/*", + "stdlib/**/*", + ]), + includes = [ + "include", + "stdlib/public/SwiftShims", + ], linkopts = [ "-lm", "-lz", @@ -19,7 +30,6 @@ cc_library( ], "//conditions:default": [], }), - includes = ["include", "stdlib/public/SwiftShims"], visibility = ["//visibility:public"], ) diff --git a/swift/xcode-autobuilder/tests/BUILD.bazel b/swift/xcode-autobuilder/tests/BUILD.bazel index 579f96546cd..3013dd47a10 100644 --- a/swift/xcode-autobuilder/tests/BUILD.bazel +++ b/swift/xcode-autobuilder/tests/BUILD.bazel @@ -1,21 +1,28 @@ [ py_test( - name = test_dir + '-test', + name = test_dir + "-test", size = "small", - srcs = ['autobuild_tester.py'], - main = 'autobuild_tester.py', + srcs = ["autobuild_tester.py"], + args = [ + "$(location //swift/xcode-autobuilder)", + "$(location %s)" % test_dir, + ], data = [ "//swift/xcode-autobuilder", test_dir, - ] + glob([test_dir + '/**/*']), - args = [ - '$(location //swift/xcode-autobuilder)', - '$(location %s)' % test_dir, - ] + ] + glob([test_dir + "/**/*"]), + main = "autobuild_tester.py", + ) + for test_dir in glob( + ["*"], + exclude = [ + "*.*", + ".*", + ], + exclude_directories = 0, ) - for test_dir in glob(["*"], exclude_directories=0, exclude=['*.*', '.*']) ] test_suite( - name='tests' + name = "tests", )