Merge branch 'main' into swift/case-let-dataflow

This commit is contained in:
Nora Dimitrijević
2023-03-28 18:20:33 +02:00
committed by GitHub
100 changed files with 1497 additions and 734 deletions

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -1039,6 +1039,29 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
or
b = bRight and origdelta = odRight and reason = rRight and bLeft instanceof SemZeroBound
)
or
exists(
SemRemExpr rem, SemZeroBound b1, SemZeroBound b2, D::Delta d_max, D::Delta d1, D::Delta d2,
boolean fbe1, boolean fbe2, D::Delta od1, D::Delta od2, SemReason r1, SemReason r2
|
rem = e and
not (upper = true and semPositive(rem.getRightOperand())) and
not (upper = true and semPositive(rem.getLeftOperand())) and
boundedRemExpr(rem, b1, true, d1, fbe1, od1, r1) and
boundedRemExpr(rem, b2, false, d2, fbe2, od2, r2) and
(
if D::toFloat(d1).abs() > D::toFloat(d2).abs()
then (
b = b1 and d_max = d1 and fromBackEdge = fbe1 and origdelta = od1 and reason = r1
) else (
b = b2 and d_max = d2 and fromBackEdge = fbe2 and origdelta = od2 and reason = r2
)
)
|
upper = true and delta = D::fromFloat(D::toFloat(d_max).abs() - 1)
or
upper = false and delta = D::fromFloat(-D::toFloat(d_max).abs() + 1)
)
)
}
@@ -1065,4 +1088,11 @@ module RangeStage<DeltaSig D, BoundSig<D> Bounds, LangSig<D> LangParam, UtilSig<
bounded(add.getRightOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
)
}
private predicate boundedRemExpr(
SemRemExpr rem, SemZeroBound b, boolean upper, D::Delta delta, boolean fromBackEdge,
D::Delta origdelta, SemReason reason
) {
bounded(rem.getRightOperand(), b, delta, upper, fromBackEdge, origdelta, reason)
}
}

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -18,20 +18,20 @@ int test2(struct List* p) {
int count = 0;
for (; p; p = p->next) {
count = (count+1) % 10;
range(count); // $ range=<=9
range(count); // $ range=>=-9 range=<=9
}
range(count); // $ range=<=9
range(count); // $ range=>=-9 range=<=9
return count;
}
int test3(struct List* p) {
int count = 0;
for (; p; p = p->next) {
range(count++); // $ range=<=9
range(count++); // $ range=>=-9 range=<=9
count = count % 10;
range(count); // $ range=<=9
range(count); // $ range=>=-9 range=<=9
}
range(count); // $ range=<=9
range(count); // $ range=>=-9 range=<=9
return count;
}
@@ -960,7 +960,22 @@ void guard_bound_out_of_range(void) {
void test_mod(int s) {
int s2 = s % 5;
range(s2); // $ range=<=4 // -4 .. 4
range(s2); // $ range=>=-4 range=<=4
}
void test_mod_neg(int s) {
int s2 = s % -5;
range(s2); // $ range=>=-4 range=<=4
}
void test_mod_ternary(int s, bool b) {
int s2 = s % (b ? 5 : 500);
range(s2); // $ range=>=-499 range=<=499
}
void test_mod_ternary2(int s, bool b1, bool b2) {
int s2 = s % (b1 ? (b2 ? 5 : -5000) : -500000);
range(s2); // $ range=>=-499999 range=<=499999
}
void exit(int);

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -11,6 +11,13 @@ class WebConfigXml extends XmlFile {
WebConfigXml() { this.getName().matches("%Web.config") }
}
/**
* A `Web.config` transformation file.
*/
class WebConfigReleaseTransformXml extends XmlFile {
WebConfigReleaseTransformXml() { this.getName().matches("%Web.Release.config") }
}
/** DEPRECATED: Alias for WebConfigXml */
deprecated class WebConfigXML = WebConfigXml;
@@ -19,6 +26,11 @@ class ConfigurationXmlElement extends XmlElement {
ConfigurationXmlElement() { this.getName().toLowerCase() = "configuration" }
}
/** A `<compilation>` tag in an ASP.NET configuration file. */
class CompilationXmlElement extends XmlElement {
CompilationXmlElement() { this.getName().toLowerCase() = "compilation" }
}
/** DEPRECATED: Alias for ConfigurationXmlElement */
deprecated class ConfigurationXMLElement = ConfigurationXmlElement;
@@ -149,3 +161,15 @@ class HttpCookiesElement extends XmlElement {
/** DEPRECATED: Alias for isRequireSsl */
deprecated predicate isRequireSSL() { this.isRequireSsl() }
}
/** A `Transform` attribute in a Web.config transformation file. */
class TransformXmlAttribute extends XmlAttribute {
TransformXmlAttribute() { this.getName().toLowerCase() = "transform" }
/**
* Gets the list of attribute removals in `Transform=RemoveAttributes(list)`.
*/
string getRemoveAttributes() {
result = this.getValue().regexpCapture("RemoveAttributes\\((.*)\\)", 1).splitAt(",")
}
}

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -19,6 +19,17 @@ import semmle.code.asp.WebConfig
from SystemWebXmlElement web, XmlAttribute debugAttribute
where
debugAttribute = web.getAChild("compilation").getAttribute("debug") and
not debugAttribute.getValue().toLowerCase() = "false"
exists(CompilationXmlElement compilation | compilation.getParent() = web |
debugAttribute = compilation.getAttribute("debug") and
not debugAttribute.getValue().toLowerCase() = "false"
) and
not exists(
TransformXmlAttribute attribute, CompilationXmlElement compilation,
WebConfigReleaseTransformXml file
|
compilation = attribute.getElement() and
file = compilation.getFile() and
attribute.getRemoveAttributes() = "debug" and
file.getParentContainer() = web.getFile().getParentContainer()
)
select debugAttribute, "The 'debug' flag is set for an ASP.NET configuration file."

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* The query `cs/web/debug-binary` now disregards the `debug` attribute in case there is a transformation that removes it.

View File

@@ -1 +1,2 @@
| bad/Web.config:4:5:7:7 | debug=true | The 'debug' flag is set for an ASP.NET configuration file. |
| bad1/Web.config:4:5:7:7 | debug=true | The 'debug' flag is set for an ASP.NET configuration file. |
| bad2/Web.config:4:5:7:7 | debug=true | The 'debug' flag is set for an ASP.NET configuration file. |

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation
defaultLanguage="c#"
debug="true"
/>
</system.web>
</configuration>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<compilation
defaultLanguage="c#"
debug="true"
/>
</system.web>
</configuration>

View File

@@ -45,4 +45,3 @@ Experiment and learn how to write effective and efficient queries for CodeQL dat
- :doc:`Working with source locations <working-with-source-locations>`: You can use the location of entities within Java code to look for potential errors. Locations allow you to deduce the presence, or absence, of white space which, in some cases, may indicate a problem.
- :doc:`Abstract syntax tree classes for working with Java programs <abstract-syntax-tree-classes-for-working-with-java-programs>`: CodeQL has a large selection of classes for representing the abstract syntax tree of Java programs.

View File

@@ -0,0 +1,417 @@
.. _customizing-library-models-for-java:
:orphan:
:nosearch:
Customizing Library Models for Java
===================================
.. include:: ../reusables/beta-note-customizing-library-models.rst
The Java analysis can be customized by adding library models (summaries, sinks and sources) in data extension files.
A model is a definition of a behavior of a library element, such as a method, that is used to improve the data flow analysis precision by identifying more results.
Most of the security related queries are taint tracking queries that try to find paths from a source of untrusted input to a sink that represents a vulnerability. Sources are the starting points of a taint tracking data flow analysis, and sinks are the end points of a taint tracking data flow analysis.
Furthermore, the taint tracking queries also need to know how data can flow through elements that are not included in the source code. These are named summaries: they are models of elements that allow us to synthesize the elements flow behavior without having them in the source code. This is especially helpful when using a third party (or the standard) library.
The models are defined using data extensions where each tuple constitutes a model.
A data extension file for Java is a YAML file in the form:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: <name of extensible predicate>
data:
- <tuple1>
- <tuple2>
- ...
Data extensions contribute to the extensible predicates defined in the CodeQL library. For more information on how to define data extensions and extensible predicates as well as how to wire them up, see the :ref:`data-extensions` documentation.
The CodeQL library for Java exposes the following extensible predicates:
- **sourceModel**\(package, type, subtypes, name, signature, ext, output, kind, provenance). This is used for **source** models.
- **sinkModel**\(package, type, subtypes, name, signature, ext, input, kind, provenance). This is used for **sink** models.
- **summaryModel**\(package, type, subtypes, name, signature, ext, input, output, kind, provenance). This is used for **summary** models.
- **neutralModel**\(package, type, name, signature, provenance). This is used for **neutral** models, which only have minor impact on the data flow analysis.
The extensible predicates are populated using data extensions specified in YAML files.
In the sections below, we will provide examples of how to add tuples to the different extensible predicates.
The extensible predicates are used to customize and improve the existing data flow queries, by providing sources, sinks, and flow through (summaries) for library elements.
The :ref:`reference-material` section will provide details on the *mini DSLs* that define models for each extensible predicate.
Example: Taint sink in the **java.sql** package
------------------------------------------------
In this example we will show how to model the argument of the **execute** method as a SQL injection sink.
This is the **execute** method in the **Statement** class, which is located in the **java.sql** package.
Note that this sink is already added to the CodeQL Java analysis.
.. code-block:: java
public static void taintsink(Connection conn, String query) throws SQLException {
Statement stmt = conn.createStatement();
stmt.execute(query); // The argument to this method is a SQL injection sink.
}
We need to add a tuple to the **sinkModel**\(package, type, subtypes, name, signature, ext, input, kind, provenance) extensible predicate. To do this, add the following to a data extension file:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: sinkModel
data:
- ["java.sql", "Statement", True, "execute", "(String)", "", "Argument[0]", "sql", "manual"]
Since we are adding a new sink, we need to add a tuple to the **sinkModel** extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a sink.
- The first value **java.sql** is the package name.
- The second value **Statement** is the name of the class (type) that contains the method.
- The third value **True** is a flag that indicates whether or not the sink also applies to all overrides of the method.
- The fourth value **execute** is the method name.
- The fifth value **(String)** is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
The remaining values are used to define the **access path**, the **kind**, and the **provenance** (origin) of the sink.
- The seventh value **Argument[0]** is the **access path** to the first argument passed to the method, which means that this is the location of the sink.
- The eighth value **sql** is the kind of the sink. The sink kind is used to define the queries where the sink is in scope. In this case - the SQL injection queries.
- The ninth value **manual** is the provenance of the sink, which is used to identify the origin of the sink.
Example: Taint source from the **java.net** package
----------------------------------------------------
In this example we show how to model the return value from the **getInputStream** method as a **remote** source.
This is the **getInputStream** method in the **Socket** class, which is located in the **java.net** package.
Note that this source is already added to the CodeQL Java analysis.
.. code-block:: java
public static void tainted(Socket socket) throws IOException {
InputStream stream = socket.getInputStream(); // The return value of this method is a remote source of taint.
...
}
We need to add a tuple to the **sourceModel**\(package, type, subtypes, name, signature, ext, output, kind, provenance) extensible predicate. To do this, add the following to a data extension file:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: sourceModel
data:
- ["java.net", "Socket", False, "getInputStream", "()", "", "ReturnValue", "remote", "manual"]
Since we are adding a new source, we need to add a tuple to the **sourceModel** extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a source.
- The first value **java.net** is the package name.
- The second value **Socket** is the name of the class (type) that contains the source.
- The third value **False** is a flag that indicates whether or not the source also applies to all overrides of the method.
- The fourth value **getInputStream** is the method name.
- The fifth value **()** is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
The remaining values are used to define the **access path**, the **kind**, and the **provenance** (origin) of the source.
- The seventh value **ReturnValue** is the access path to the return of the method, which means that it is the return value that should be considered a source of tainted input.
- The eighth value **remote** is the kind of the source. The source kind is used to define the queries where the source is in scope. **remote** applies to many of the security related queries as it means a remote source of untrusted data. As an example the SQL injection query uses **remote** sources.
- The ninth value **manual** is the provenance of the source, which is used to identify the origin of the source.
Example: Add flow through the **concat** method
------------------------------------------------
In this example we show how to model flow through a method for a simple case.
This pattern covers many of the cases where we need to define flow through a method.
Note that the flow through the **concat** method is already added to the CodeQL Java analysis.
.. code-block:: java
public static void taintflow(String s1, String s2) {
String t = s1.concat(s2); // There is taint flow from s1 and s2 to t.
...
}
We need to add tuples to the **summaryModel**\(package, type, subtypes, name, signature, ext, input, output, kind, provenance) extensible predicate. To do this, add the following to a data extension file:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[this]", "ReturnValue", "taint", "manual"]
- ["java.lang", "String", False, "concat", "(String)", "", "Argument[0]", "ReturnValue", "taint", "manual"]
Reasoning:
Since we are adding flow through a method, we need to add tuples to the **summaryModel** extensible predicate.
Each tuple defines flow from one argument to the return value.
The first row defines flow from the qualifier (**s1** in the example) to the return value (**t** in the example) and the second row defines flow from the first argument (**s2** in the example) to the return value (**t** in the example).
The first five values identify the callable (in this case a method) to be modeled as a summary.
These are the same for both of the rows above as we are adding two summaries for the same method.
- The first value **java.lang** is the package name.
- The second value **String** is the class (type) name.
- The third value **False** is a flag that indicates whether or not the summary also applies to all overrides of the method.
- The fourth value **concat** is the method name.
- The fifth value **(String)** is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
The remaining values are used to define the **access path**, the **kind**, and the **provenance** (origin) of the summary.
- The seventh value is the access path to the input (where data flows from). **Argument[this]** is the access path to the qualifier (**s1** in the example) and **Argument[0]** is the access path to the first argument (**s2** in the example).
- The eighth value **ReturnValue** is the access path to the output (where data flows to), in this case **ReturnValue**, which means that the input flows to the return value.
- The ninth value **taint** is the kind of the flow. **taint** means that taint is propagated through the call.
- The tenth value **manual** is the provenance of the summary, which is used to identify the origin of the summary.
Example: Add flow through the **map** method
---------------------------------------------
In this example, we will see a more complex example of modeling flow through a method.
This pattern shows how to model flow through higher order methods and collection types.
Note that the flow through the **map** method is already added to the CodeQL Java analysis.
.. code-block:: java
public static void taintflow(Stream<String> s) {
Stream<String> l = s.map(e -> e.concat("\n"));
...
}
To do this, add the following to a data extension file:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: summaryModel
data:
- ["java.util.stream", "Stream", True, "map", "(Function)", "", "Argument[this].Element", "Argument[0].Parameter[0]", "value", "manual"]
- ["java.util.stream", "Stream", True, "map", "(Function)", "", "Argument[0].ReturnValue", "ReturnValue.Element", "value", "manual"]
Since we are adding flow through a method, we need to add tuples to the **summaryModel** extensible predicate.
Each tuple defines part of the flow that comprises the total flow through the **map** method.
The first five values identify the callable (in this case a method) to be modeled as a summary.
These are the same for both of the rows above as we are adding two summaries for the same method.
- The first value **java.util.stream** is the package name.
- The second value **Stream** is the class (type) name.
- The third value **True** is a flag that indicates whether or not the summary also applies to all overrides of the method.
- The fourth value **map** is the method name.
- The fifth value **Function** is the method input type signature.
The sixth value should be left empty and is out of scope for this documentation.
The remaining values are used to define the **access path**, the **kind**, and the **provenance** (origin) of the summary definition.
- The seventh value is the access path to the **input** (where data flows from).
- The eighth value is the access path to the **output** (where data flows to).
For the first row:
- The seventh value is **Argument[this].Element**, which is the access path to the elements of the qualifier (the elements of the stream **s** in the example).
- The eight value is **Argument[0].Parameter[0]**, which is the access path to the first parameter of the **Function** argument of **map** (the lambda parameter **e** in the example).
For the second row:
- The seventh value is **Argument[0].ReturnValue**, which is the access path to the return value of the **Function** argument of **map** (the return value of the lambda in the example).
- The eighth value is **ReturnValue.Element**, which is the access path to the elements of the return value of **map** (the elements of the stream **l** in the example).
For the remaining values for both rows:
- The ninth value **value** is the kind of the flow. **value** means that the value is preserved.
- The tenth value **manual** is the provenance of the summary, which is used to identify the origin of the summary.
That is, the first row models that there is value flow from the elements of the qualifier stream into the first argument of the function provided to **map** and the second row models that there is value flow from the return value of the function to the elements of the stream returned from **map**.
Example: Add a **neutral** method
----------------------------------
In this example we will show how to model the **now** method as being neutral.
A neutral model is used to define that there is no flow through a method.
Note that the neutral model for the **now** method is already added to the CodeQL Java analysis.
.. code-block:: java
public static void taintflow() {
Instant t = Instant.now(); // There is no flow from now to t.
...
}
We need to add a tuple to the **neutralModel**\(package, type, name, signature, provenance) extensible predicate. To do this, add the following to a data extension file:
.. code-block:: yaml
extensions:
- addsTo:
pack: codeql/java-all
extensible: neutralModel
data:
- ["java.time", "Instant", "now", "()", "manual"]
Since we are adding a neutral model, we need to add tuples to the **neutralModel** extensible predicate.
The first five values identify the callable (in this case a method) to be modeled as a neutral and the fifth value is the provenance (origin) of the neutral.
- The first value **java.time** is the package name.
- The second value **Instant** is the class (type) name.
- The third value **now** is the method name.
- The fourth value **()** is the method input type signature.
- The fifth value **manual** is the provenance of the neutral.
.. _reference-material:
Reference material
------------------
The following sections provide reference material for extensible predicates.
This includes descriptions of each of the arguments (e.g. access paths, kinds and provenance).
Extensible predicates
---------------------
Below is a description of the columns for each extensible predicate.
Sources, sinks, summaries and neutrals are commonly known as models.
The semantics of many of the columns of the extensible predicates are shared.
The shared columns are:
- **package**: Name of the package containing the element(s) to be modeled.
- **type**: Name of the type containing the element(s) to be modeled.
- **subtypes**: A boolean flag indicating whether the model should also apply to all overrides of the selected element(s).
- **name**: Name of the element (optional). If this is left blank, it means all elements matching the previous selection criteria.
- **signature**: Type signature of the selected element (optional). If this is left blank, it means all elements matching the previous selection criteria.
- **ext**: Specifies additional API-graph-like edges (mostly empty) and out of scope for this document.
- **provenance**: Provenance (origin) of the model definition.
The columns **package**, **type**, **subtypes**, **name**, and **signature** are used to select the element(s) that the model applies to.
The :ref:`access-paths` section describes how access paths are composed.
This is the most complicated part of the extensible predicates and the **mini DSL** for access paths is shared across all extensible predicates.
sourceModel(package, type, subtypes, name, signature, ext, output, kind, provenance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Taint source. Most taint tracking queries will use all sources added to this extensible predicate regardless of their kind.
- **output**: Access path to the source, where the possibly tainted data flows from.
- **kind**: Kind of the source.
- **provenance**: Provenance (origin) of the source definition.
As most sources are used by all taint tracking queries there are only a few different source kinds.
The following source kinds are supported:
- **remote**: A remote source of possibly tainted data. This is the most common kind for a source. Sources of this kind are used for almost all taint tracking queries.
Below is an enumeration of the remaining source kinds, but they are out of scope for this documentation:
- **contentprovider**, **android-widget**, **android-external-storage-dir**.
sinkModel(package, type, subtypes, name, signature, ext, input, kind, provenance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Taint sink. As opposed to source kinds, there are many different kinds of sinks as these tend to be more query specific.
- **input**: Access path to the sink, where we want to check if tainted data can flow into.
- **kind**: Kind of the sink.
The following sink kinds are supported:
- **sql**: A SQL injection vulnerability sink.
- **xss**: A cross-site scripting vulnerability sink.
- **logging**: A log output sink.
Below is an enumeration of the remaining sinks, but they are out of scope for this documentation:
- **open-url**, **jndi-injection**, **ldap**, **jdbc-url**
- **mvel**, **xpath**, **groovy**, **ognl-injection**
- **intent-start**, **pending-intent-sent**, **url-open-stream**, **url-redirect**
- **create-file**, **read-file**, **write-file**, **set-hostname-verifier**
- **header-splitting**, **information-leak**, **xslt**, **jexl**
- **bean-validation**, **ssti**, **fragment-injection**, **regex-use[**\ `arg`\ **]**
summaryModel(package, type, subtypes, name, signature, ext, input, output, kind, provenance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Flow through (summary). This extensible predicate is used to model flow through elements.
- **input**: Access path to the input of the element (where data will flow from to the output).
- **output**: Access path to the output of the element (where data will flow to from the input).
- **kind**: Kind of the flow through.
- **provenance**: Provenance (origin) of the flow through.
The following kinds are supported:
- **taint**: This means the output is not necessarily equal to the input, but it was derived from the input in an unrestrictive way. An attacker who controls the input will have significant control over the output as well.
- **value**: This means that the output equals the input or a copy of the input such that all of its properties are preserved.
neutralModel(package, type, name, signature, provenance)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This extensible predicate is not typically needed externally, but included here for completeness.
It only has minor impact on the data flow analysis.
Manual neutrals are considered high confidence dispatch call targets and can reduce the number of dispatch call targets during data flow analysis (a performance optimization).
- **provenance**: Provenance (origin) of the flow through.
.. _access-paths:
Access paths
------------
The **input**, and **output** columns consist of a **.**-separated list of components, which is evaluated from left to right, with each step selecting a new set of values derived from the previous set of values.
The following components are supported:
- **Argument[**\ `n`\ **]** selects the argument at index `n` (zero-indexed).
- **Argument[**\ `this`\ **]** selects the qualifier (instance parameter).
- **Argument[**\ `n1..n2`\ **]** selects the arguments in the given range (both ends included).
- **Parameter[**\ `n`\ **]** selects the parameter at index `n` (zero-indexed).
- **Parameter[**\ `n1..n2`\ **]** selects the parameters in the given range (both ends included).
- **ReturnValue** selects the return value.
- **Field[**\ `name`\ **]** selects the field with the fully qualified name `name`.
- **SyntheticField[**\ `name`\ **]** selects the synthetic field with name `name`.
- **SyntheticGlobal[**\ `name`\ **]** selects the synthetic global with name `name`.
- **ArrayElement** selects the elements of an array.
- **Element** selects the elements of a collection-like container.
- **MapKey** selects the element keys of a map.
- **MapValue** selects the element values of a map.
Provenance
----------
The **provenance** column is used to specify the provenance (origin) of the model definition and how the model was verified.
The following values are supported:
- **manual**: The model was manually created and added to the extensible predicate.
or values in the form **origin-verification**, where origin is one of:
- **ai**: The model was generated by AI.
- **df**: The model was generated by the dataflow model generator.
- **tb**: The model was generated by the type based model generator.
- **hq**: The model was generated using a heuristic query.
and verification is one of:
- **manual**: The model was verified by a human.
- **generated**: The model was generated, but not verified by a human.
The provenance is used to distinguish between models that are manually added (or verified) to the extensible predicate and models that are automatically generated.
Furthermore, it impacts the data flow analysis in the following way:
- A **manual** model takes precedence over **generated** models. If a **manual** model exists for an element then all **generated** models are ignored.
- A **generated** model is ignored during analysis, if the source code of the element it is modeling is available.
That is, generated models are less trusted than manual models and only used if neither source code nor a manual model is available.
.. include:: ../reusables/data-extensions.rst

View File

@@ -75,12 +75,12 @@ This query uses ``Call`` and ``Name`` to find calls to the function ``eval`` - w
select call, "call to 'eval'."
The ``Call`` class represents calls in Python. The ``Call.getFunc()`` predicate gets the expression being called. ``Name.getId()`` gets the identifier (as a string) of the ``Name`` expression.
Due to the dynamic nature of Python, this query will select any call of the form ``eval(...)`` regardless of whether it is a call to the built-in function ``eval`` or not.
In a later tutorial we will see how to use the type-inference library to find calls to the built-in function ``eval`` regardless of name of the variable called.
This query will select any call of the form ``eval(...)`` regardless of whether it is a call to the built-in function ``eval`` or not.
Due to the dynamic nature of Python, such syntactic queries can be inaccurate. If one is looking for invocations of the built-in function ``eval``,
it is preferred to use the API graph, see ":doc:`Using API graphs in Python <using-api-graphs-in-python>`."
Further reading
---------------
.. include:: ../reusables/python-further-reading.rst
.. include:: ../reusables/codeql-ref-tools-further-reading.rst

View File

@@ -0,0 +1,7 @@
.. pull-quote::
Beta Notice - Unstable API
Library customization using data extensions is currently in beta and subject to change.
Breaking changes to this format may occur while in beta.

View File

@@ -0,0 +1,10 @@
.. _data-extensions:
Data Extensions
===============
.. pull-quote::
Beta Notice - Internal documentation
Data extensions are documented internally in a `Google doc <https://docs.google.com/document/d/14IYCHX8wWuU-HTvJ2gPSdXQKHKYbWCHQKOgn8oLaa80>`_.

View File

@@ -11,11 +11,11 @@
Microsoft extensions (up to VS 2019),
Arm Compiler 5 [3]_","``.cpp``, ``.c++``, ``.cxx``, ``.hpp``, ``.hh``, ``.h++``, ``.hxx``, ``.c``, ``.cc``, ``.h``"
C#,C# up to 10.0,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
C#,C# up to 11,"Microsoft Visual Studio up to 2019 with .NET up to 4.8,
.NET Core up to 3.1
.NET 5, .NET 6","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
.NET 5, .NET 6, .NET 7","``.sln``, ``.csproj``, ``.cs``, ``.cshtml``, ``.xaml``"
Go (aka Golang), "Go up to 1.20", "Go 1.11 or more recent", ``.go``
Java,"Java 7 to 20 [4]_","javac (OpenJDK and Oracle JDK),

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -33,17 +33,15 @@ freemarker.cache,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,
freemarker.template,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,7,,,,,,,,,,,,
groovy.lang,26,,,,,,26,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
groovy.util,5,,,,,,5,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
hudson.model,4,,1,,2,,,,,,,,,,,,,1,,1,,,,,,,,,,,,,,,,,,,,,1,
hudson.os,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
hudson.remoting,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
hudson.util,4,,1,,3,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,1,
hudson,44,,16,,19,,,,,,,,,,,,,6,,17,,,,,,,,,,,,,2,,,,,,,,16,
io.netty.bootstrap,3,,,,,,,,,,,,,,,,,3,,,,,,,,,,,,,,,,,,,,,,,,
io.netty.channel,9,,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,,,,,,,,,
io.netty.handler.codec.http,3,,1,,,,,,,,,,,,,,,2,,1,,,,,,,,,,,,,,,,,,,,,1,
io.netty.buffer,,,207,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,130,77
io.netty.channel,9,2,,,,,,,,,,,,,,,,9,,,,,,,,,,,,,,,,,,,,,,2,,
io.netty.handler.codec,3,13,260,,,,,,,,,,,,,,,2,,1,,,,,,,,,,,,,,,,,,,,13,144,116
io.netty.handler.ssl,2,,,,,,,,,,,,,,,,,,,2,,,,,,,,,,,,,,,,,,,,,,
io.netty.handler.stream,1,,,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,
io.netty.resolver,,,1,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,1,
io.netty.util.internal,2,,2,,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,2,
io.netty.util,2,,23,,1,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,21,2
jakarta.faces.context,2,7,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,2,,,,7,,
jakarta.json,,,123,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,100,23
jakarta.ws.rs.client,1,,,,,,,,,,,,,,,,,1,,,,,,,,,,,,,,,,,,,,,,,,
1 package sink source summary sink:bean-validation sink:create-file sink:fragment-injection sink:groovy sink:header-splitting sink:information-leak sink:intent-start sink:jdbc-url sink:jexl sink:jndi-injection sink:ldap sink:logging sink:mvel sink:ognl-injection sink:open-url sink:pending-intent-sent sink:read-file sink:regex-use sink:regex-use[-1] sink:regex-use[0] sink:regex-use[] sink:regex-use[f-1] sink:regex-use[f1] sink:regex-use[f] sink:set-hostname-verifier sink:sql sink:ssti sink:url-open-stream sink:url-redirect sink:write-file sink:xpath sink:xslt sink:xss source:android-external-storage-dir source:android-widget source:contentprovider source:remote summary:taint summary:value
33 freemarker.template 7 7
34 groovy.lang 26 26
35 groovy.util 5 5
36 hudson.model hudson 4 44 1 16 2 19 1 6 1 17 2 1 16
hudson.os 1 1
hudson.remoting 1 1
hudson.util 4 1 3 1 1
37 io.netty.bootstrap 3 3
38 io.netty.channel io.netty.buffer 9 207 9 130 77
39 io.netty.handler.codec.http io.netty.channel 3 9 2 1 2 9 1 2 1
40 io.netty.handler.codec 3 13 260 2 1 13 144 116
41 io.netty.handler.ssl 2 2
42 io.netty.handler.stream 1 1
43 io.netty.resolver 1 1
44 io.netty.util.internal io.netty.util 2 2 23 1 1 2 21 2
45 jakarta.faces.context 2 7 2 7
46 jakarta.json 123 100 23
47 jakarta.ws.rs.client 1 1

View File

@@ -22,6 +22,6 @@ Java framework & library support
Java extensions,"``javax.*``, ``jakarta.*``",63,611,34,1,,4,,1,1,2
Kotlin Standard Library,``kotlin*``,,1835,12,10,,,,,,2
`Spring <https://spring.io/>`_,``org.springframework.*``,29,480,101,,,,19,14,,29
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.hubspot.jinjava``, ``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.util``, ``hudson.model``, ``hudson.os``, ``hudson.remoting``, ``hudson.util``, ``io.netty.bootstrap``, ``io.netty.channel``, ``io.netty.handler.codec.http``, ``io.netty.handler.ssl``, ``io.netty.handler.stream``, ``io.netty.resolver``, ``io.netty.util.internal``, ``javafx.scene.web``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.hive.metastore``, ``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.geogebra.web.full.main``, ``org.hibernate``, ``org.jdbi.v3.core``, ``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``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",60,314,328,12,,,18,18,,28
Totals,,217,8544,1647,150,6,10,113,33,1,113
Others,"``cn.hutool.core.codec``, ``com.esotericsoftware.kryo.io``, ``com.esotericsoftware.kryo5.io``, ``com.fasterxml.jackson.core``, ``com.fasterxml.jackson.databind``, ``com.hubspot.jinjava``, ``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.util``, ``hudson``, ``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``, ``jodd.json``, ``net.sf.saxon.s9api``, ``ognl``, ``okhttp3``, ``org.apache.commons.codec``, ``org.apache.commons.compress.archivers.tar``, ``org.apache.commons.jelly``, ``org.apache.commons.jexl2``, ``org.apache.commons.jexl3``, ``org.apache.commons.logging``, ``org.apache.commons.ognl``, ``org.apache.directory.ldap.client.api``, ``org.apache.hadoop.hive.metastore``, ``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.geogebra.web.full.main``, ``org.hibernate``, ``org.jdbi.v3.core``, ``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``, ``play.mvc``, ``ratpack.core.form``, ``ratpack.core.handling``, ``ratpack.core.http``, ``ratpack.exec``, ``ratpack.form``, ``ratpack.func``, ``ratpack.handling``, ``ratpack.http``, ``ratpack.util``, ``retrofit2``",75,813,364,26,,,18,18,,33
Totals,,232,9043,1683,164,6,10,113,33,1,118

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -0,0 +1,128 @@
/** Provides classes to reason about insecure LDAP authentication. */
import java
private import semmle.code.java.dataflow.DataFlow
private import semmle.code.java.frameworks.Networking
private import semmle.code.java.frameworks.Jndi
/**
* An expression that represents an insecure (non-SSL, non-private) LDAP URL.
*/
class InsecureLdapUrl extends Expr {
InsecureLdapUrl() {
this instanceof InsecureLdapUrlLiteral
or
// Concatentation of insecure protcol and non-private host:
// protocol + host + ...
exists(AddExpr e, CompileTimeConstantExpr protocol, Expr rest, Expr host |
e = this and
protocol = e.getLeftOperand() and
rest = e.getRightOperand() and
if rest instanceof AddExpr then host = rest.(AddExpr).getLeftOperand() else host = rest
|
protocol.getStringValue() = "ldap://" and
not exists(string hostString | hostString = getHostname(host) |
hostString.length() = 0 or // Empty host is loopback address
hostString instanceof PrivateHostName
)
)
}
}
/**
* A sink representing the construction of a `DirContextEnvironment`.
*/
class InsecureLdapUrlSink extends DataFlow::Node {
InsecureLdapUrlSink() {
exists(ConstructorCall cc |
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
this.asExpr() = cc.getArgument(0)
)
}
}
/**
* Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`.
*/
predicate isBasicAuthEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.authentication", "simple") or
hasFieldNameEnv(ma, "SECURITY_AUTHENTICATION", "simple")
}
/**
* Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`.
*/
predicate isSslEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.protocol", "ssl") or
hasFieldNameEnv(ma, "SECURITY_PROTOCOL", "ssl")
}
/**
* Holds if `ma` writes the `java.naming.provider.url` (also known as `Context.PROVIDER_URL`) key of a `Hashtable`.
*/
predicate isProviderUrlSetter(MethodAccess ma) {
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
(
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url"
or
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName("PROVIDER_URL") and
f.getDeclaringType() instanceof TypeNamingContext
)
)
}
/**
* An insecure (non-SSL, non-private) LDAP URL string literal.
*/
private class InsecureLdapUrlLiteral extends StringLiteral {
InsecureLdapUrlLiteral() {
// Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives.
exists(string s | this.getValue() = s |
s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and
not s.substring(7, s.length()) instanceof PrivateHostName
)
}
}
/** The class `java.util.Hashtable`. */
private class TypeHashtable extends Class {
TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") }
}
/** Get the string value of an expression representing a hostname. */
private string getHostname(Expr expr) {
result = expr.(CompileTimeConstantExpr).getStringValue() or
result =
expr.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
}
/**
* Holds if `ma` sets `fieldValue` to `envValue` in some `Hashtable`.
*/
bindingset[fieldValue, envValue]
private predicate hasFieldValueEnv(MethodAccess ma, string fieldValue, string envValue) {
// environment.put("java.naming.security.authentication", "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}
/**
* Holds if `ma` sets attribute name `fieldName` to `envValue` in some `Hashtable`.
*/
bindingset[fieldName, envValue]
private predicate hasFieldNameEnv(MethodAccess ma, string fieldName, string envValue) {
// environment.put(Context.SECURITY_AUTHENTICATION, "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName(fieldName) and
f.getDeclaringType() instanceof TypeNamingContext
) and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}

View File

@@ -0,0 +1,59 @@
/** Provides dataflow configurations to reason about insecure LDAP authentication. */
import java
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.dataflow.TaintTracking
import semmle.code.java.frameworks.Jndi
import semmle.code.java.security.InsecureLdapAuth
/**
* A taint-tracking configuration for `ldap://` URL in LDAP authentication.
*/
module InsecureLdapUrlConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl }
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
/** Method call of `env.put()`. */
predicate isAdditionalFlowStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
pred.asExpr() = ma.getArgument(1) and
isProviderUrlSetter(ma) and
succ.asExpr() = ma.getQualifier()
)
}
}
module InsecureLdapUrlFlow = TaintTracking::Global<InsecureLdapUrlConfig>;
/**
* A taint-tracking configuration for `simple` basic-authentication in LDAP configuration.
*/
private module BasicAuthConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isBasicAuthEnv(ma) and
ma.getQualifier() = src.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
)
}
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
}
module BasicAuthFlow = DataFlow::Global<BasicAuthConfig>;
/**
* A taint-tracking configuration for `ssl` configuration in LDAP authentication.
*/
private module RequiresSslConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isSslEnv(ma) and
ma.getQualifier() = src.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr()
)
}
predicate isSink(DataFlow::Node sink) { sink instanceof InsecureLdapUrlSink }
}
module RequiresSslFlow = DataFlow::Global<RequiresSslConfig>;

View File

@@ -0,0 +1,51 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>
When using the Java LDAP API to perform LDAPv3-style extended operations
and controls, a context with connection properties including user
credentials is started. Transmission of LDAP credentials in cleartext
allows remote attackers to obtain sensitive information by sniffing the
network.
</p>
</overview>
<recommendation>
<p>
Use the <code>ldaps://</code> protocol to send credentials through SSL or
use SASL authentication.
</p>
</recommendation>
<example>
<p>
In the following (bad) example, a <code>ldap://</code> URL is used and
credentials will be sent in plaintext.
</p>
<sample src="LdapAuthUseLdap.java"/>
<p>
In the following (good) example, a <code>ldaps://</code> URL is used so
credentials will be encrypted with SSL.
</p>
<sample src="LdapAuthUseLdaps.java"/>
<p>
In the following (good) example, a <code>ldap://</code> URL is used, but
SASL authentication is enabled so that the credentials will be encrypted.
</p>
<sample src="LdapEnableSasl.java"/>
</example>
<references>
<li>
Oracle:
<a href="https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html">LDAP and LDAPS URLs</a>
</li>
<li>
Oracle:
<a href="https://docs.oracle.com/javase/tutorial/jndi/ldap/simple.html">Simple authentication</a>
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,25 @@
/**
* @name Insecure LDAP authentication
* @description LDAP authentication with credentials sent in cleartext makes sensitive information vulnerable to remote attackers
* @kind path-problem
* @problem.severity warning
* @security-severity 8.8
* @precision medium
* @id java/insecure-ldap-auth
* @tags security
* experimental
* external/cwe/cwe-522
* external/cwe/cwe-319
*/
import java
import semmle.code.java.security.InsecureLdapAuthQuery
import InsecureLdapUrlFlow::PathGraph
from InsecureLdapUrlFlow::PathNode source, InsecureLdapUrlFlow::PathNode sink
where
InsecureLdapUrlFlow::flowPath(source, sink) and
BasicAuthFlow::flowTo(sink.getNode()) and
not RequiresSslFlow::flowTo(sink.getNode())
select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(),
"LDAP connection string"

View File

@@ -0,0 +1,9 @@
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);

View File

@@ -0,0 +1,9 @@
String ldapUrl = "ldaps://ad.your-server.com:636";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);

View File

@@ -0,0 +1,9 @@
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 GSSAPI");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* The query `java/insecure-ldap-auth` has been promoted from experimental to the main query pack. This query detects transmission of cleartext credentials in LDAP authentication. Insecure LDAP authentication causes sensitive information to be vulnerable to remote attackers. This query was originally [submitted as an experimental query by @luchua-bc](https://github.com/github/codeql/pull/4854)

View File

@@ -1,24 +0,0 @@
public class InsecureLdapAuth {
/** LDAP authentication */
public DirContext ldapAuth(String ldapUserName, String password) {
{
// BAD: LDAP authentication in cleartext
String ldapUrl = "ldap://ad.your-server.com:389";
}
{
// GOOD: LDAPS authentication over SSL
String ldapUrl = "ldaps://ad.your-server.com:636";
}
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);
return dirContext;
}
}

View File

@@ -1,27 +0,0 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>When using the Java LDAP API to perform LDAPv3-style extended operations and controls, a context with connection properties including user credentials is started. Transmission of LDAP credentials in cleartext allows remote attackers to obtain sensitive information by sniffing the network.</p>
</overview>
<recommendation>
<p>Use LDAPS to send credentials through SSL or use SASL authentication.</p>
</recommendation>
<example>
<p>The following example shows two ways of using LDAP authentication. In the 'BAD' case, the credentials are transmitted in cleartext. In the 'GOOD' case, the credentials are transmitted over SSL.</p>
<sample src="InsecureLdapAuth.java" />
</example>
<references>
<li>
Oracle:
<a href="https://docs.oracle.com/javase/jndi/tutorial/ldap/misc/url.html">LDAP and LDAPS URLs</a>
</li>
<li>
Oracle:
<a href="https://docs.oracle.com/javase/tutorial/jndi/ldap/simple.html">Simple authentication</a>
</li>
</references>
</qhelp>

View File

@@ -1,211 +0,0 @@
/**
* @name Insecure LDAP authentication
* @description LDAP authentication with credentials sent in cleartext.
* @kind path-problem
* @problem.severity warning
* @precision medium
* @id java/insecure-ldap-auth
* @tags security
* experimental
* external/cwe/cwe-522
* external/cwe/cwe-319
*/
import java
import DataFlow
import semmle.code.java.frameworks.Jndi
import semmle.code.java.frameworks.Networking
import semmle.code.java.dataflow.TaintTracking
import DataFlow::PathGraph
/**
* Insecure (non-SSL, non-private) LDAP URL string literal.
*/
class InsecureLdapUrlLiteral extends StringLiteral {
InsecureLdapUrlLiteral() {
// Match connection strings with the LDAP protocol and without private IP addresses to reduce false positives.
exists(string s | this.getValue() = s |
s.regexpMatch("(?i)ldap://[\\[a-zA-Z0-9].*") and
not s.substring(7, s.length()) instanceof PrivateHostName
)
}
}
/** The class `java.util.Hashtable`. */
class TypeHashtable extends Class {
TypeHashtable() { this.getSourceDeclaration().hasQualifiedName("java.util", "Hashtable") }
}
/**
* Holds if a non-private LDAP string is concatenated from both protocol and host.
*/
predicate concatInsecureLdapString(Expr protocol, Expr host) {
protocol.(CompileTimeConstantExpr).getStringValue() = "ldap://" and
not exists(string hostString |
hostString = host.(CompileTimeConstantExpr).getStringValue() or
hostString =
host.(VarAccess).getVariable().getAnAssignedValue().(CompileTimeConstantExpr).getStringValue()
|
hostString.length() = 0 or // Empty host is loopback address
hostString instanceof PrivateHostName
)
}
/** Gets the leftmost operand in a concatenated string */
Expr getLeftmostConcatOperand(Expr expr) {
if expr instanceof AddExpr
then result = getLeftmostConcatOperand(expr.(AddExpr).getLeftOperand())
else result = expr
}
/**
* String concatenated with `InsecureLdapUrlLiteral`.
*/
class InsecureLdapUrl extends Expr {
InsecureLdapUrl() {
this instanceof InsecureLdapUrlLiteral
or
concatInsecureLdapString(this.(AddExpr).getLeftOperand(),
getLeftmostConcatOperand(this.(AddExpr).getRightOperand()))
}
}
/**
* Holds if `ma` writes the `java.naming.provider.url` (also known as `Context.PROVIDER_URL`) key of a `Hashtable`.
*/
predicate isProviderUrlSetter(MethodAccess ma) {
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
(
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = "java.naming.provider.url"
or
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName("PROVIDER_URL") and
f.getDeclaringType() instanceof TypeNamingContext
)
)
}
/**
* Holds if `ma` sets `fieldValue` to `envValue` in some `Hashtable`.
*/
bindingset[fieldValue, envValue]
predicate hasFieldValueEnv(MethodAccess ma, string fieldValue, string envValue) {
// environment.put("java.naming.security.authentication", "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
ma.getArgument(0).(CompileTimeConstantExpr).getStringValue() = fieldValue and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}
/**
* Holds if `ma` sets attribute name `fieldName` to `envValue` in some `Hashtable`.
*/
bindingset[fieldName, envValue]
predicate hasFieldNameEnv(MethodAccess ma, string fieldName, string envValue) {
// environment.put(Context.SECURITY_AUTHENTICATION, "simple")
ma.getMethod().getDeclaringType().getAnAncestor() instanceof TypeHashtable and
ma.getMethod().hasName(["put", "setProperty"]) and
exists(Field f |
ma.getArgument(0) = f.getAnAccess() and
f.hasName(fieldName) and
f.getDeclaringType() instanceof TypeNamingContext
) and
ma.getArgument(1).(CompileTimeConstantExpr).getStringValue() = envValue
}
/**
* Holds if `ma` sets `java.naming.security.authentication` (also known as `Context.SECURITY_AUTHENTICATION`) to `simple` in some `Hashtable`.
*/
predicate isBasicAuthEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.authentication", "simple") or
hasFieldNameEnv(ma, "SECURITY_AUTHENTICATION", "simple")
}
/**
* Holds if `ma` sets `java.naming.security.protocol` (also known as `Context.SECURITY_PROTOCOL`) to `ssl` in some `Hashtable`.
*/
predicate isSslEnv(MethodAccess ma) {
hasFieldValueEnv(ma, "java.naming.security.protocol", "ssl") or
hasFieldNameEnv(ma, "SECURITY_PROTOCOL", "ssl")
}
/**
* A taint-tracking configuration for `ldap://` URL in LDAP authentication.
*/
class InsecureUrlFlowConfig extends TaintTracking::Configuration {
InsecureUrlFlowConfig() { this = "InsecureLdapAuth:InsecureUrlFlowConfig" }
/** Source of `ldap://` connection string. */
override predicate isSource(DataFlow::Node src) { src.asExpr() instanceof InsecureLdapUrl }
/** Sink of directory context creation. */
override predicate isSink(DataFlow::Node sink) {
exists(ConstructorCall cc |
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
sink.asExpr() = cc.getArgument(0)
)
}
/** Method call of `env.put()`. */
override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
exists(MethodAccess ma |
pred.asExpr() = ma.getArgument(1) and
isProviderUrlSetter(ma) and
succ.asExpr() = ma.getQualifier()
)
}
}
/**
* A taint-tracking configuration for `simple` basic-authentication in LDAP configuration.
*/
class BasicAuthFlowConfig extends DataFlow::Configuration {
BasicAuthFlowConfig() { this = "InsecureLdapAuth:BasicAuthFlowConfig" }
/** Source of `simple` configuration. */
override predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isBasicAuthEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr()
)
}
/** Sink of directory context creation. */
override predicate isSink(DataFlow::Node sink) {
exists(ConstructorCall cc |
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
sink.asExpr() = cc.getArgument(0)
)
}
}
/**
* A taint-tracking configuration for `ssl` configuration in LDAP authentication.
*/
class SslFlowConfig extends DataFlow::Configuration {
SslFlowConfig() { this = "InsecureLdapAuth:SSLFlowConfig" }
/** Source of `ssl` configuration. */
override predicate isSource(DataFlow::Node src) {
exists(MethodAccess ma |
isSslEnv(ma) and ma.getQualifier() = src.(PostUpdateNode).getPreUpdateNode().asExpr()
)
}
/** Sink of directory context creation. */
override predicate isSink(DataFlow::Node sink) {
exists(ConstructorCall cc |
cc.getConstructedType().getAnAncestor() instanceof TypeDirContext and
sink.asExpr() = cc.getArgument(0)
)
}
}
from DataFlow::PathNode source, DataFlow::PathNode sink, InsecureUrlFlowConfig config
where
config.hasFlowPath(source, sink) and
exists(BasicAuthFlowConfig bc | bc.hasFlowTo(sink.getNode())) and
not exists(SslFlowConfig sc | sc.hasFlowTo(sink.getNode()))
select sink.getNode(), source, sink, "Insecure LDAP authentication from $@.", source.getNode(),
"LDAP connection string"

View File

@@ -1,111 +0,0 @@
edges
| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String |
| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment |
| InsecureLdapAuth.java:15:3:15:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:20:49:20:59 | environment |
| InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String | InsecureLdapAuth.java:15:3:15:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:20:49:20:59 | environment |
| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String |
| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment |
| InsecureLdapAuth.java:29:3:29:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:34:49:34:59 | environment |
| InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String | InsecureLdapAuth.java:29:3:29:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:34:49:34:59 | environment |
| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:48:49:48:59 | environment |
| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String |
| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | InsecureLdapAuth.java:63:49:63:59 | environment |
| InsecureLdapAuth.java:57:3:57:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:63:49:63:59 | environment |
| InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String | InsecureLdapAuth.java:57:3:57:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment |
| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:63:49:63:59 | environment |
| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String |
| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:77:49:77:59 | environment |
| InsecureLdapAuth.java:72:3:72:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:77:49:77:59 | environment |
| InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String | InsecureLdapAuth.java:72:3:72:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:91:49:91:59 | environment |
| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String |
| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment |
| InsecureLdapAuth.java:100:3:100:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:105:59:105:69 | environment |
| InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String | InsecureLdapAuth.java:100:3:100:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:105:59:105:69 | environment |
| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String |
| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment |
| InsecureLdapAuth.java:115:3:115:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:120:49:120:59 | environment |
| InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String | InsecureLdapAuth.java:115:3:115:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | InsecureLdapAuth.java:120:49:120:59 | environment |
| InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable |
| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String |
| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | InsecureLdapAuth.java:142:50:142:60 | environment |
| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment |
| InsecureLdapAuth.java:140:3:140:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:142:50:142:60 | environment |
| InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String | InsecureLdapAuth.java:140:3:140:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:142:50:142:60 | environment |
| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String |
| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:153:50:153:60 | environment |
| InsecureLdapAuth.java:151:3:151:13 | environment [post update] [<map.value>] : String | InsecureLdapAuth.java:153:50:153:60 | environment |
| InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String | InsecureLdapAuth.java:151:3:151:13 | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | InsecureLdapAuth.java:153:50:153:60 | environment |
nodes
| InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
| InsecureLdapAuth.java:15:3:15:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:15:41:15:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:17:3:17:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:20:49:20:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | semmle.label | ... + ... : String |
| InsecureLdapAuth.java:29:3:29:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:29:41:29:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:31:3:31:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:34:49:34:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:45:3:45:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:48:49:48:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:53:20:53:50 | "ldap://ad.your-server.com:636" : String | semmle.label | "ldap://ad.your-server.com:636" : String |
| InsecureLdapAuth.java:57:3:57:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:57:41:57:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:59:3:59:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:62:3:62:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:63:49:63:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:68:20:68:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
| InsecureLdapAuth.java:72:3:72:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:72:41:72:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:77:49:77:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:88:3:88:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:91:49:91:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
| InsecureLdapAuth.java:100:3:100:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:100:41:100:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:102:3:102:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment |
| InsecureLdapAuth.java:105:59:105:69 | environment | semmle.label | environment |
| InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | semmle.label | "ldap://ad.your-server.com:389" : String |
| InsecureLdapAuth.java:115:3:115:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:115:47:115:53 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:117:3:117:13 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:120:49:120:59 | environment | semmle.label | environment |
| InsecureLdapAuth.java:124:3:124:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable |
| InsecureLdapAuth.java:128:3:128:5 | env [post update] : Hashtable | semmle.label | env [post update] : Hashtable |
| InsecureLdapAuth.java:135:20:135:39 | ... + ... : String | semmle.label | ... + ... : String |
| InsecureLdapAuth.java:137:10:137:20 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:140:3:140:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:140:41:140:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:141:16:141:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:142:50:142:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | semmle.label | ... + ... : String |
| InsecureLdapAuth.java:151:3:151:13 | environment [post update] [<map.value>] : String | semmle.label | environment [post update] [<map.value>] : String |
| InsecureLdapAuth.java:151:41:151:47 | ldapUrl : String | semmle.label | ldapUrl : String |
| InsecureLdapAuth.java:152:16:152:26 | environment [post update] : Hashtable | semmle.label | environment [post update] : Hashtable |
| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment |
| InsecureLdapAuth.java:153:50:153:60 | environment | semmle.label | environment |
subpaths
#select
| InsecureLdapAuth.java:20:49:20:59 | environment | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:20:49:20:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:11:20:11:50 | "ldap://ad.your-server.com:389" | LDAP connection string |
| InsecureLdapAuth.java:34:49:34:59 | environment | InsecureLdapAuth.java:25:20:25:39 | ... + ... : String | InsecureLdapAuth.java:34:49:34:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:25:20:25:39 | ... + ... | LDAP connection string |
| InsecureLdapAuth.java:105:59:105:69 | environment | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:105:59:105:69 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:96:20:96:50 | "ldap://ad.your-server.com:389" | LDAP connection string |
| InsecureLdapAuth.java:120:49:120:59 | environment | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" : String | InsecureLdapAuth.java:120:49:120:59 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:111:20:111:50 | "ldap://ad.your-server.com:389" | LDAP connection string |
| InsecureLdapAuth.java:153:50:153:60 | environment | InsecureLdapAuth.java:147:20:147:39 | ... + ... : String | InsecureLdapAuth.java:153:50:153:60 | environment | Insecure LDAP authentication from $@. | InsecureLdapAuth.java:147:20:147:39 | ... + ... | LDAP connection string |

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-522/InsecureLdapAuth.ql

View File

@@ -11,13 +11,13 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);
DirContext dirContext = new InitialDirContext(environment); // $ hasInsecureLdapAuth
}
// BAD - Test LDAP authentication in cleartext using `DirContext`.
@@ -25,13 +25,13 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://"+serverName+":389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
DirContext dirContext = new InitialDirContext(environment);
DirContext dirContext = new InitialDirContext(environment); // $ hasInsecureLdapAuth
}
// GOOD - Test LDAP authentication over SSL.
@@ -39,7 +39,7 @@ public class InsecureLdapAuth {
String ldapUrl = "ldaps://ad.your-server.com:636";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
@@ -53,7 +53,7 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://ad.your-server.com:636";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
@@ -68,7 +68,7 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "DIGEST-MD5 GSSAPI");
@@ -82,7 +82,7 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://localhost:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
@@ -96,14 +96,14 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
environment.put(Context.REFERRAL, "follow");
environment.put(Context.SECURITY_AUTHENTICATION, "simple");
environment.put(Context.SECURITY_PRINCIPAL, ldapUserName);
environment.put(Context.SECURITY_CREDENTIALS, password);
InitialLdapContext ldapContext = new InitialLdapContext(environment, null);
}
InitialLdapContext ldapContext = new InitialLdapContext(environment, null); // $ hasInsecureLdapAuth
}
// BAD - Test LDAP authentication in cleartext using `DirContext` and string literals.
@@ -111,13 +111,13 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://ad.your-server.com:389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put("java.naming.factory.initial",
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put("java.naming.provider.url", ldapUrl);
environment.put("java.naming.referral", "follow");
environment.put("java.naming.security.authentication", "simple");
environment.put("java.naming.security.principal", ldapUserName);
environment.put("java.naming.security.credentials", password);
DirContext dirContext = new InitialDirContext(environment);
DirContext dirContext = new InitialDirContext(environment); // $ hasInsecureLdapAuth
}
private void setSSL(Hashtable env) {
@@ -136,7 +136,7 @@ public class InsecureLdapAuth {
Hashtable<String, String> environment = new Hashtable<String, String>();
setSSL(environment);
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
setBasicAuth(environment, ldapUserName, password);
DirContext dirContext = new InitialLdapContext(environment, null);
@@ -147,9 +147,9 @@ public class InsecureLdapAuth {
String ldapUrl = "ldap://"+serverName+":389";
Hashtable<String, String> environment = new Hashtable<String, String>();
environment.put(Context.INITIAL_CONTEXT_FACTORY,
"com.sun.jndi.ldap.LdapCtxFactory");
"com.sun.jndi.ldap.LdapCtxFactory");
environment.put(Context.PROVIDER_URL, ldapUrl);
setBasicAuth(environment, ldapUserName, password);
DirContext dirContext = new InitialLdapContext(environment, null);
DirContext dirContext = new InitialLdapContext(environment, null); // $ hasInsecureLdapAuth
}
}

View File

@@ -0,0 +1,20 @@
import java
import semmle.code.java.security.InsecureLdapAuthQuery
import TestUtilities.InlineExpectationsTest
class InsecureLdapAuthenticationTest extends InlineExpectationsTest {
InsecureLdapAuthenticationTest() { this = "InsecureLdapAuthentication" }
override string getARelevantTag() { result = "hasInsecureLdapAuth" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsecureLdapAuth" and
exists(DataFlow::Node sink | InsecureLdapUrlFlow::flowTo(sink) |
BasicAuthFlow::flowTo(sink) and
not RequiresSslFlow::flowTo(sink) and
sink.getLocation() = location and
element = sink.toString() and
value = ""
)
}
}

View File

@@ -243,6 +243,11 @@ module AccessPath {
root.isGlobal()
)
or
exists(Assignment assign |
fromReference(assign.getLhs().flow(), root) = result and
node = assign.getRhs().flow()
)
or
exists(FunctionDeclStmt fun |
node = DataFlow::valueNode(fun) and
result = fun.getIdentifier().(GlobalVarDecl).getName() and

View File

@@ -85,6 +85,7 @@ test_getAFunctionValue
| es2015.js:35:1:35:3 | sum | es2015.js:31:1:33:1 | functio ... +y+z;\\n} |
| es2015.js:36:1:36:3 | sum | es2015.js:31:1:33:1 | functio ... +y+z;\\n} |
| m2.js:2:6:2:18 | function() {} | m2.js:2:6:2:18 | function() {} |
| m.js:1:1:1:9 | exports.f | m.js:1:13:1:25 | function() {} |
| m.js:1:1:1:25 | exports ... on() {} | m.js:1:13:1:25 | function() {} |
| m.js:1:13:1:25 | function() {} | m.js:1:13:1:25 | function() {} |
| m.js:2:1:2:9 | exports.f | m.js:1:13:1:25 | function() {} |
@@ -100,16 +101,19 @@ test_getAFunctionValue
| protoclass.js:3:10:3:10 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:4:3:4:11 | this.init | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:7:1:7:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:7:1:7:16 | F.prototype.init | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:7:1:11:1 | F.proto ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:7:20:11:1 | functio ... m();\\n} | protoclass.js:7:20:11:1 | functio ... m();\\n} |
| protoclass.js:8:3:8:13 | this.method | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:9:11:9:21 | this.method | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:13:1:13:1 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| protoclass.js:13:1:13:18 | F.prototype.method | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:13:1:13:34 | F.proto ... on() {} | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:13:22:13:34 | function() {} | protoclass.js:13:22:13:34 | function() {} |
| protoclass.js:15:16:15:16 | F | protoclass.js:3:1:5:1 | functio ... it();\\n} |
| reflection.js:1:1:3:1 | functio ... x+y;\\n} | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:5:3:5:5 | add | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:5:3:5:11 | add.apply | reflection.js:5:15:5:39 | functio ... n 56; } |
| reflection.js:5:3:5:39 | add.app ... n 56; } | reflection.js:5:15:5:39 | functio ... n 56; } |
| reflection.js:5:15:5:14 | this | reflection.js:1:1:3:1 | functio ... x+y;\\n} |
| reflection.js:5:15:5:39 | functio ... n 56; } | reflection.js:5:15:5:39 | functio ... n 56; } |
@@ -163,11 +167,13 @@ test_getAFunctionValue
| tst.js:42:2:42:26 | functio ... rn x; } | tst.js:42:2:42:26 | functio ... rn x; } |
| tst.js:44:1:44:15 | function A() {} | tst.js:44:1:44:15 | function A() {} |
| tst.js:45:1:45:1 | A | tst.js:44:1:44:15 | function A() {} |
| tst.js:45:1:45:13 | A.prototype.f | tst.js:45:17:47:1 | functio ... .g();\\n} |
| tst.js:45:1:47:1 | A.proto ... .g();\\n} | tst.js:45:17:47:1 | functio ... .g();\\n} |
| tst.js:45:17:47:1 | functio ... .g();\\n} | tst.js:45:17:47:1 | functio ... .g();\\n} |
| tst.js:46:2:46:7 | this.g | tst.js:48:17:48:29 | function() {} |
| tst.js:46:2:46:7 | this.g | tst.js:61:17:61:29 | function() {} |
| tst.js:48:1:48:1 | A | tst.js:44:1:44:15 | function A() {} |
| tst.js:48:1:48:13 | A.prototype.g | tst.js:48:17:48:29 | function() {} |
| tst.js:48:1:48:29 | A.proto ... on() {} | tst.js:48:17:48:29 | function() {} |
| tst.js:48:17:48:29 | function() {} | tst.js:48:17:48:29 | function() {} |
| tst.js:50:1:50:15 | function B() {} | tst.js:50:1:50:15 | function B() {} |
@@ -186,11 +192,13 @@ test_getAFunctionValue
| tst.js:60:1:60:1 | C | tst.js:59:1:59:15 | function C() {} |
| tst.js:60:19:60:19 | A | tst.js:44:1:44:15 | function A() {} |
| tst.js:61:1:61:1 | C | tst.js:59:1:59:15 | function C() {} |
| tst.js:61:1:61:13 | C.prototype.g | tst.js:61:17:61:29 | function() {} |
| tst.js:61:1:61:29 | C.proto ... on() {} | tst.js:61:17:61:29 | function() {} |
| tst.js:61:17:61:29 | function() {} | tst.js:61:17:61:29 | function() {} |
| tst.js:63:1:67:2 | (functi ... f();\\n}) | tst.js:63:2:67:1 | functio ... .f();\\n} |
| tst.js:63:2:67:1 | functio ... .f();\\n} | tst.js:63:2:67:1 | functio ... .f();\\n} |
| tst.js:64:17:64:17 | B | tst.js:50:1:50:15 | function B() {} |
| tst.js:65:5:65:7 | b.f | tst.js:65:11:65:23 | function() {} |
| tst.js:65:5:65:23 | b.f = function() {} | tst.js:65:11:65:23 | function() {} |
| tst.js:65:11:65:23 | function() {} | tst.js:65:11:65:23 | function() {} |
| tst.js:66:5:66:7 | b.f | tst.js:52:5:54:2 | functio ... g();\\n\\t} |

View File

@@ -12,6 +12,7 @@ test_ApiObject
test_Connection
| client.js:1:10:1:27 | exportedConnection |
| tst.js:7:15:7:18 | conn |
| tst.js:8:5:8:19 | this.connection |
| tst.js:11:5:11:19 | this.connection |
| tst.js:16:10:16:49 | api.cha ... ction() |
| tst.js:19:7:19:21 | getConnection() |
@@ -20,7 +21,9 @@ test_Connection
| tst.js:48:7:48:21 | getConnection() |
| tst.js:54:37:54:51 | getConnection() |
| tst.js:57:14:57:48 | config. ... ction') |
| tst.js:62:3:62:36 | MyAppli ... nection |
| tst.js:62:40:62:79 | api.cha ... ction() |
| tst.js:63:3:63:34 | MyAppli ... onflict |
| tst.js:63:38:63:77 | api.cha ... ction() |
| tst.js:67:14:67:47 | MyAppli ... nection |
| tst.js:78:35:78:49 | getConnection() |
@@ -41,6 +44,7 @@ test_Connection
| tst.js:118:12:118:26 | getConnection() |
| tst.js:120:21:120:24 | conn |
| tst.js:126:22:126:25 | conn |
| tst_conflict.js:6:3:6:34 | MyAppli ... onflict |
| tst_conflict.js:6:38:6:77 | api.cha ... ction() |
test_DataCallback
| client.js:3:28:3:34 | x => {} |

View File

@@ -11,6 +11,7 @@ apiObject
| tst_conflict.js:6:38:6:58 | api.cha ... hain2() |
connection
| type tracker with call steps | tst.js:7:15:7:18 | conn |
| type tracker with call steps | tst.js:8:5:8:19 | this.connection |
| type tracker with call steps | tst.js:11:5:11:19 | this.connection |
| type tracker with call steps | tst.js:80:16:80:19 | conn |
| type tracker with call steps | tst.js:84:22:84:22 | x |
@@ -30,7 +31,9 @@ connection
| type tracker without call steps | tst.js:48:7:48:21 | getConnection() |
| type tracker without call steps | tst.js:54:37:54:51 | getConnection() |
| type tracker without call steps | tst.js:57:14:57:48 | config. ... ction') |
| type tracker without call steps | tst.js:62:3:62:36 | MyAppli ... nection |
| type tracker without call steps | tst.js:62:40:62:79 | api.cha ... ction() |
| type tracker without call steps | tst.js:63:3:63:34 | MyAppli ... onflict |
| type tracker without call steps | tst.js:63:38:63:77 | api.cha ... ction() |
| type tracker without call steps | tst.js:67:14:67:47 | MyAppli ... nection |
| type tracker without call steps | tst.js:78:35:78:49 | getConnection() |
@@ -43,6 +46,7 @@ connection
| type tracker without call steps | tst.js:118:12:118:26 | getConnection() |
| type tracker without call steps | tst.js:120:21:120:24 | conn |
| type tracker without call steps | tst.js:126:22:126:25 | conn |
| type tracker without call steps | tst_conflict.js:6:3:6:34 | MyAppli ... onflict |
| type tracker without call steps | tst_conflict.js:6:38:6:77 | api.cha ... ction() |
| type tracker without call steps with property conflict | tst.js:63:3:63:25 | MyAppli ... mespace |
| type tracker without call steps with property conflict | tst_conflict.js:6:3:6:25 | MyAppli ... mespace |

View File

@@ -3227,10 +3227,13 @@ getRouteHandlerContainerStep
| src/route-collection.js:1:18:4:1 | {\\n a: ... (req)\\n} | src/route-collection.js:3:6:3:35 | (req, r ... og(req) | src/advanced-routehandler-registration.js:116:14:116:30 | importedRoutes[p] |
| src/route-collection.js:1:18:4:1 | {\\n a: ... (req)\\n} | src/route-collection.js:3:6:3:35 | (req, r ... og(req) | src/advanced-routehandler-registration.js:119:14:119:29 | importedRoutes.b |
dbUse
| src/middleware-flow.js:6:5:6:10 | req.db |
| src/middleware-flow.js:6:5:6:21 | req.db = new DB() |
| src/middleware-flow.js:6:14:6:21 | new DB() |
| src/middleware-flow.js:7:5:7:15 | req.deep.db |
| src/middleware-flow.js:7:5:7:26 | req.dee ... ew DB() |
| src/middleware-flow.js:7:19:7:26 | new DB() |
| src/middleware-flow.js:8:5:8:22 | req.deep.access.db |
| src/middleware-flow.js:8:5:8:33 | req.dee ... ew DB() |
| src/middleware-flow.js:8:26:8:33 | new DB() |
| src/middleware-flow.js:18:9:18:14 | req.db |

View File

@@ -159,6 +159,11 @@ nodes
| xss-through-dom.js:141:25:141:27 | src |
| xss-through-dom.js:150:24:150:26 | src |
| xss-through-dom.js:150:24:150:26 | src |
| xss-through-dom.js:154:25:154:27 | msg |
| xss-through-dom.js:155:27:155:29 | msg |
| xss-through-dom.js:155:27:155:29 | msg |
| xss-through-dom.js:159:34:159:52 | $("textarea").val() |
| xss-through-dom.js:159:34:159:52 | $("textarea").val() |
edges
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
| forms.js:8:23:8:28 | values | forms.js:9:31:9:36 | values |
@@ -263,6 +268,10 @@ edges
| xss-through-dom.js:139:11:139:52 | src | xss-through-dom.js:150:24:150:26 | src |
| xss-through-dom.js:139:17:139:52 | documen ... k").src | xss-through-dom.js:139:11:139:52 | src |
| xss-through-dom.js:139:17:139:52 | documen ... k").src | xss-through-dom.js:139:11:139:52 | src |
| xss-through-dom.js:154:25:154:27 | msg | xss-through-dom.js:155:27:155:29 | msg |
| xss-through-dom.js:154:25:154:27 | msg | xss-through-dom.js:155:27:155:29 | msg |
| xss-through-dom.js:159:34:159:52 | $("textarea").val() | xss-through-dom.js:154:25:154:27 | msg |
| xss-through-dom.js:159:34:159:52 | $("textarea").val() | xss-through-dom.js:154:25:154:27 | msg |
#select
| forms.js:9:31:9:40 | values.foo | forms.js:8:23:8:28 | values | forms.js:9:31:9:40 | values.foo | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:8:23:8:28 | values | DOM text |
| forms.js:12:31:12:40 | values.bar | forms.js:11:24:11:29 | values | forms.js:12:31:12:40 | values.bar | $@ is reinterpreted as HTML without escaping meta-characters. | forms.js:11:24:11:29 | values | DOM text |
@@ -307,3 +316,4 @@ edges
| xss-through-dom.js:140:19:140:21 | src | xss-through-dom.js:139:17:139:52 | documen ... k").src | xss-through-dom.js:140:19:140:21 | src | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:139:17:139:52 | documen ... k").src | DOM text |
| xss-through-dom.js:141:25:141:27 | src | xss-through-dom.js:139:17:139:52 | documen ... k").src | xss-through-dom.js:141:25:141:27 | src | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:139:17:139:52 | documen ... k").src | DOM text |
| xss-through-dom.js:150:24:150:26 | src | xss-through-dom.js:139:17:139:52 | documen ... k").src | xss-through-dom.js:150:24:150:26 | src | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:139:17:139:52 | documen ... k").src | DOM text |
| xss-through-dom.js:155:27:155:29 | msg | xss-through-dom.js:159:34:159:52 | $("textarea").val() | xss-through-dom.js:155:27:155:29 | msg | $@ is reinterpreted as HTML without escaping meta-characters. | xss-through-dom.js:159:34:159:52 | $("textarea").val() | DOM text |

View File

@@ -148,4 +148,15 @@ const cashDom = require("cash-dom");
cashDom("#id").html(DOMPurify ? DOMPurify.sanitize(src) : src); // OK
$("<a />", { html: src }).appendTo("#id"); // NOT OK
function foo() {
window.VeryUniqueXssTestName = {
send: function (msg) {
$("#id").html(msg); // NOT OK
},
};
VeryUniqueXssTestName.send($("textarea").val());
}
foo()
})();

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -11,16 +11,57 @@
*/
import python
import IsComparisons
from Compare comp, Cmpop op, ClassValue c, string alt
where
invalid_portable_is_comparison(comp, op, c) and
not cpython_interned_constant(comp.getASubExpression()) and
(
op instanceof Is and alt = "=="
/** Holds if the comparison `comp` uses `is` or `is not` (represented as `op`) to compare its `left` and `right` arguments. */
predicate comparison_using_is(Compare comp, ControlFlowNode left, Cmpop op, ControlFlowNode right) {
exists(CompareNode fcomp | fcomp = comp.getAFlowNode() |
fcomp.operands(left, op, right) and
(op instanceof Is or op instanceof IsNot)
)
}
private predicate cpython_interned_value(Expr e) {
exists(string text | text = e.(StrConst).getText() |
text.length() = 0
or
op instanceof IsNot and alt = "!="
text.length() = 1 and text.regexpMatch("[U+0000-U+00ff]")
)
or
exists(int i | i = e.(IntegerLiteral).getN().toInt() | -5 <= i and i <= 256)
or
exists(Tuple t | t = e and not exists(t.getAnElt()))
}
predicate uninterned_literal(Expr e) {
(
e instanceof StrConst
or
e instanceof IntegerLiteral
or
e instanceof FloatLiteral
or
e instanceof Dict
or
e instanceof List
or
e instanceof Tuple
) and
not cpython_interned_value(e)
}
from Compare comp, Cmpop op, string alt
where
exists(ControlFlowNode left, ControlFlowNode right |
comparison_using_is(comp, left, op, right) and
(
op instanceof Is and alt = "=="
or
op instanceof IsNot and alt = "!="
)
|
uninterned_literal(left.getNode())
or
uninterned_literal(right.getNode())
)
select comp,
"Values compared using '" + op.getSymbol() +

View File

@@ -0,0 +1,4 @@
---
category: fix
---
* Fixed some accidental predicate visibility in the backwards-compatible wrapper for data flow configurations. In particular `DataFlow::hasFlowPath`, `DataFlow::hasFlow`, `DataFlow::hasFlowTo`, and `DataFlow::hasFlowToExpr` were accidentally exposed in a single version.

View File

@@ -43,11 +43,16 @@ class MethodCallSynth extends MethodCallImpl, TMethodCallSynth {
final override AstNode getReceiverImpl() { synthChild(this, 0, result) }
final override AstNode getArgumentImpl(int n) { synthChild(this, n + 1, result) and n >= 0 }
final override AstNode getArgumentImpl(int n) {
synthChild(this, n + 1, result) and
n in [0 .. this.getNumberOfArgumentsImpl() - 1]
}
final override int getNumberOfArgumentsImpl() { this = TMethodCallSynth(_, _, _, _, result) }
final override Block getBlockImpl() { synthChild(this, -2, result) }
final override Block getBlockImpl() {
synthChild(this, this.getNumberOfArgumentsImpl() + 1, result)
}
}
class IdentifierMethodCall extends MethodCallImpl, TIdentifierMethodCall {

View File

@@ -21,6 +21,8 @@ private module Cached {
qName = getAnAssumedGlobalConst()
or
qName = namespaceDeclaration(_)
or
qName = getAnAssumedGlobalNamespacePrefix(_)
} or
TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) }
@@ -36,6 +38,8 @@ private module Cached {
TResolved(container) = resolveConstantReadAccess(n.getScopeExpr()) and
result = scopeAppend(container, n.getName())
)
or
result = getAnAssumedGlobalNamespacePrefix(n)
}
cached
@@ -407,12 +411,35 @@ private module ResolveImpl {
*/
string getAnAssumedGlobalConst() {
exists(ConstantAccess access |
not exists(access.getScopeExpr()) and
result = access.getName() and
isToplevel(access)
)
}
private ConstantAccess getANamespaceScopeInTopLevel() {
result.(Namespace).getEnclosingModule() instanceof Toplevel
or
result = getANamespaceScopeInTopLevel().getScopeExpr()
}
/**
* Gets the syntactical qualified name of the given constant access, which must be a top-level
* namespace or scope prefix thereof.
*
* For example, for `module A::B::C` this gets `A`, `A::B`, and `A::B::C` for the two prefixes
* and the module itself, respectively.
*/
string getAnAssumedGlobalNamespacePrefix(ConstantAccess access) {
access = getANamespaceScopeInTopLevel() and
(
not exists(access.getScopeExpr()) and
result = access.getName()
or
result =
scopeAppend(getAnAssumedGlobalNamespacePrefix(access.getScopeExpr()), access.getName())
)
}
pragma[nomagic]
private string isDefinedConstantNonRec(string container, string name) {
result = resolveConstantWriteAccessNonRec(_, container, name)
@@ -420,6 +447,12 @@ private module ResolveImpl {
result = [builtin(), getAnAssumedGlobalConst()] and
name = result and
container = "Object"
or
exists(ConstantAccess access |
container = getAnAssumedGlobalNamespacePrefix(access.getScopeExpr()) and
name = access.getName() and
result = getAnAssumedGlobalNamespacePrefix(access)
)
}
pragma[nomagic]

View File

@@ -818,7 +818,7 @@ private module AssignOperationDesugar {
i in [0 .. sao.getNumberOfArguments()]
or
parent = setter and
i = opAssignIndex + 1 and
i = opAssignIndex and
child =
SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(sao, opAssignIndex)))
)
@@ -975,7 +975,7 @@ private module DestructuredAssignDesugar {
pragma[nomagic]
private predicate destructuredAssignSynthesis(AstNode parent, int i, Child child) {
exists(DestructuredAssignExpr tae |
exists(DestructuredAssignExpr tae, int total | total = tae.getNumberOfElements() |
parent = tae and
i = -1 and
child = SynthChild(StmtSequenceKind())
@@ -998,15 +998,13 @@ private module DestructuredAssignDesugar {
)
or
parent = seq and
i = tae.getNumberOfElements() and
i = total and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(seq, tae.getNumberOfElements()) |
exists(AstNode assign | assign = TAssignExprSynth(seq, total) |
parent = assign and
i = 0 and
child =
SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae,
tae.getNumberOfElements())))
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae, total)))
or
parent = assign and
i = 1 and
@@ -1022,12 +1020,10 @@ private module DestructuredAssignDesugar {
restIndex = tae.getRestIndexOrNumberOfElements()
|
parent = seq and
i = j + 1 + tae.getNumberOfElements() and
i = j + 1 + total and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign |
assign = TAssignExprSynth(seq, j + 1 + tae.getNumberOfElements())
|
exists(AstNode assign | assign = TAssignExprSynth(seq, j + 1 + total) |
exists(LhsWithReceiver mc | mc = elem |
parent = assign and
i = 0 and
@@ -1063,9 +1059,7 @@ private module DestructuredAssignDesugar {
or
parent = TMethodCallSynth(assign, 1, _, _, _) and
i = 0 and
child =
SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae,
tae.getNumberOfElements())))
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(tae, total)))
or
j < restIndex and
parent = TMethodCallSynth(assign, 1, _, _, _) and
@@ -1086,14 +1080,14 @@ private module DestructuredAssignDesugar {
child = SynthChild(IntegerLiteralKind(j))
or
i = 1 and
child = SynthChild(IntegerLiteralKind(restIndex - tae.getNumberOfElements()))
child = SynthChild(IntegerLiteralKind(restIndex - total))
)
)
or
j > restIndex and
parent = TMethodCallSynth(assign, 1, _, _, _) and
i = 1 and
child = SynthChild(IntegerLiteralKind(j - tae.getNumberOfElements()))
child = SynthChild(IntegerLiteralKind(j - total))
)
)
)
@@ -1284,10 +1278,10 @@ private module ForLoopDesugar {
child = childRef(for.getValue()) // value is the Enumerable
or
parent = eachCall and
i = -2 and
i = 1 and
child = SynthChild(BraceBlockKind())
or
exists(Block block | block = TBraceBlockSynth(eachCall, -2) |
exists(Block block | block = TBraceBlockSynth(eachCall, 1) |
// block params
parent = block and
i = 0 and
@@ -1534,14 +1528,13 @@ private module SafeNavigationCallDesugar {
i = 1
)
or
parent = TMethodCallSynth(ifExpr, 2, _, _, _) and
(
exists(int arity | parent = TMethodCallSynth(ifExpr, 2, _, _, arity) |
i = 0 and
child = SynthChild(local)
or
child = childRef(call.getArgumentImpl(i - 1))
or
child = childRef(call.getBlockImpl()) and i = -2
child = childRef(call.getBlockImpl()) and i = arity + 1
)
)
)

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }

View File

@@ -36,8 +36,6 @@ private predicate shouldPrintAstEdge(AstNode parent, string edgeName, AstNode ch
any(PrintAstConfiguration config).shouldPrintAstEdge(parent, edgeName, child)
}
private int nonSynthIndex() { result = min([-1, any(int i | exists(getSynthChild(_, i)))]) - 1 }
newtype TPrintNode =
TPrintRegularAstNode(AstNode n) { shouldPrintNode(n) } or
TPrintRegExpNode(RE::RegExpTerm term) {
@@ -115,10 +113,23 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode {
)
}
private predicate parentIsSynthesized() {
exists(AstNode parent |
shouldPrintAstEdge(parent, _, astNode) and
parent.isSynthesized()
)
}
private int getSynthAstNodeIndex() {
not astNode.isSynthesized() and result = nonSynthIndex()
this.parentIsSynthesized() and
exists(AstNode parent |
shouldPrintAstEdge(parent, _, astNode) and
parent.isSynthesized() and
synthChild(parent, result, astNode)
)
or
astNode = getSynthChild(astNode.getParent(), result)
not this.parentIsSynthesized() and
result = 0
}
override int getOrder() {
@@ -129,8 +140,8 @@ class PrintRegularAstNode extends PrintAstNode, TPrintRegularAstNode {
|
p
order by
f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), l.getStartColumn(),
l.getEndLine(), l.getEndColumn(), p.getSynthAstNodeIndex()
f.getBaseName(), f.getAbsolutePath(), l.getStartLine(), p.getSynthAstNodeIndex(),
l.getStartColumn(), l.getEndLine(), l.getEndColumn()
)
}

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* `rb/sensitive-get-query` no longer reports flow paths from input parameters to sensitive use nodes. This avoids cases where many flow paths could be generated for a single parameter, which caused excessive paths to be generated.

View File

@@ -2,7 +2,7 @@
* @name Sensitive data read from GET request
* @description Placing sensitive data in a GET request increases the risk of
* the data being exposed to an attacker.
* @kind path-problem
* @kind problem
* @problem.severity warning
* @security-severity 6.5
* @precision high
@@ -12,12 +12,10 @@
*/
import ruby
import DataFlow::PathGraph
import codeql.ruby.security.SensitiveGetQueryQuery
import codeql.ruby.security.SensitiveActions
from DataFlow::PathNode source, DataFlow::PathNode sink, SensitiveGetQuery::Configuration config
where config.hasFlowPath(source, sink)
select source.getNode(), source, sink,
"$@ for GET requests uses query parameter as sensitive data.",
source.getNode().(SensitiveGetQuery::Source).getHandler(), "Route handler"
from DataFlow::Node source, DataFlow::Node sink, SensitiveGetQuery::Configuration config
where config.hasFlow(source, sink)
select source, "$@ for GET requests uses query parameter as sensitive data.",
source.(SensitiveGetQuery::Source).getHandler(), "Route handler"

View File

@@ -25,28 +25,28 @@ calls/calls.rb:
# 67| getReceiver: [ConstantReadAccess] X
# 226| [ForExpr] for ... in ...
# 226| getDesugared: [MethodCall] call to each
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [SelfVariableAccess] self
# 226| getBlock: [BraceBlock] { ... }
# 226| getParameter: [SimpleParameter] __synth__0__1
# 226| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 226| getStmt: [AssignExpr] ... = ...
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 226| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 226| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 227| getStmt: [MethodCall] call to baz
# 227| getReceiver: [SelfVariableAccess] self
# 226| getReceiver: [MethodCall] call to bar
# 226| getReceiver: [SelfVariableAccess] self
# 229| [ForExpr] for ... in ...
# 229| getDesugared: [MethodCall] call to each
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 229| getBlock: [BraceBlock] { ... }
# 229| getParameter: [SimpleParameter] __synth__0__1
# 229| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 229| getStmt: [AssignExpr] ... = ...
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 229| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 229| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 230| getStmt: [MethodCall] call to baz
# 230| getReceiver: [ConstantReadAccess] X
# 229| getReceiver: [MethodCall] call to bar
# 229| getReceiver: [ConstantReadAccess] X
# 249| [HashLiteral] {...}
# 249| getDesugared: [MethodCall] call to []
# 249| getReceiver: [ConstantReadAccess] Hash
@@ -65,8 +65,8 @@ calls/calls.rb:
# 316| getStmt: [SetterMethodCall] call to foo=
# 316| getReceiver: [SelfVariableAccess] self
# 316| getArgument: [AssignExpr] ... = ...
# 316| getAnOperand/getRightOperand: [IntegerLiteral] 10
# 316| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 316| getAnOperand/getRightOperand: [IntegerLiteral] 10
# 316| getStmt: [LocalVariableAccess] __synth__0
# 317| [AssignExpr] ... = ...
# 317| getDesugared: [StmtSequence] ...
@@ -75,14 +75,31 @@ calls/calls.rb:
# 317| getReceiver: [SelfVariableAccess] self
# 317| getArgument: [IntegerLiteral] 0
# 317| getArgument: [AssignExpr] ... = ...
# 317| getAnOperand/getRightOperand: [IntegerLiteral] 10
# 317| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 317| getAnOperand/getRightOperand: [IntegerLiteral] 10
# 317| getStmt: [LocalVariableAccess] __synth__0
# 318| [AssignExpr] ... = ...
# 318| getDesugared: [StmtSequence] ...
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 318| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 318| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 318| getAnOperand/getRightOperand: [MethodCall] call to foo
# 318| getReceiver: [SelfVariableAccess] self
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 318| getAnOperand/getRightOperand: [SplatExpr] * ...
# 318| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...]
# 318| getDesugared: [MethodCall] call to []
# 318| getReceiver: [ConstantReadAccess] Array
# 318| getArgument: [IntegerLiteral] 1
# 318| getArgument: [IntegerLiteral] 2
# 318| getArgument: [IntegerLiteral] 3
# 318| getArgument: [IntegerLiteral] 4
# 318| getStmt: [AssignExpr] ... = ...
# 318| getDesugared: [StmtSequence] ...
# 318| getStmt: [SetterMethodCall] call to foo=
@@ -95,9 +112,6 @@ calls/calls.rb:
# 318| getStmt: [LocalVariableAccess] __synth__0__1
# 318| getAnOperand/getLeftOperand: [MethodCall] call to foo
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 318| getStmt: [AssignExpr] ... = ...
# 318| getDesugared: [StmtSequence] ...
# 318| getStmt: [SetterMethodCall] call to bar=
# 318| getReceiver: [LocalVariableAccess] __synth__1
@@ -111,56 +125,23 @@ calls/calls.rb:
# 318| getStmt: [LocalVariableAccess] __synth__0__1
# 318| getAnOperand/getLeftOperand: [MethodCall] call to bar
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getRightOperand: [MethodCall] call to foo
# 318| getReceiver: [SelfVariableAccess] self
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 318| getStmt: [AssignExpr] ... = ...
# 318| getDesugared: [StmtSequence] ...
# 318| getStmt: [SetterMethodCall] call to []=
# 318| getReceiver: [LocalVariableAccess] __synth__2
# 318| getArgument: [IntegerLiteral] 4
# 318| getArgument: [AssignExpr] ... = ...
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 318| getAnOperand/getRightOperand: [MethodCall] call to []
# 318| getReceiver: [LocalVariableAccess] __synth__3
# 318| getArgument: [IntegerLiteral] -1
# 318| getArgument: [IntegerLiteral] 4
# 318| getStmt: [LocalVariableAccess] __synth__0__1
# 318| getAnOperand/getLeftOperand: [MethodCall] call to []
# 318| getStmt: [AssignExpr] ... = ...
# 318| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 318| getAnOperand/getRightOperand: [SplatExpr] * ...
# 318| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...]
# 318| getDesugared: [MethodCall] call to []
# 318| getReceiver: [ConstantReadAccess] Array
# 318| getArgument: [IntegerLiteral] 1
# 318| getArgument: [IntegerLiteral] 2
# 318| getArgument: [IntegerLiteral] 3
# 318| getArgument: [IntegerLiteral] 4
# 319| [AssignExpr] ... = ...
# 319| getDesugared: [StmtSequence] ...
# 319| getStmt: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] a
# 319| getAnOperand/getRightOperand: [MethodCall] call to []
# 319| getReceiver: [LocalVariableAccess] __synth__2
# 319| getArgument: [IntegerLiteral] 0
# 319| getStmt: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 319| getAnOperand/getRightOperand: [MethodCall] call to foo
# 319| getReceiver: [SelfVariableAccess] self
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 319| getStmt: [AssignExpr] ... = ...
# 319| getDesugared: [StmtSequence] ...
# 319| getStmt: [SetterMethodCall] call to []=
# 319| getReceiver: [LocalVariableAccess] __synth__1
# 319| getArgument: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 319| getAnOperand/getRightOperand: [MethodCall] call to []
# 319| getReceiver: [LocalVariableAccess] __synth__2
# 319| getArgument: [RangeLiteral] _ .. _
# 319| getBegin: [IntegerLiteral] 1
# 319| getEnd: [IntegerLiteral] -1
# 319| getArgument: [IntegerLiteral] 5
# 319| getStmt: [LocalVariableAccess] __synth__0__1
# 319| getAnOperand/getLeftOperand: [MethodCall] call to []
# 319| getStmt: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 319| getAnOperand/getRightOperand: [SplatExpr] * ...
@@ -170,34 +151,49 @@ calls/calls.rb:
# 319| getArgument: [IntegerLiteral] 1
# 319| getArgument: [IntegerLiteral] 2
# 319| getArgument: [IntegerLiteral] 3
# 319| getStmt: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] a
# 319| getAnOperand/getRightOperand: [MethodCall] call to []
# 319| getReceiver: [LocalVariableAccess] __synth__2
# 319| getArgument: [IntegerLiteral] 0
# 319| getStmt: [AssignExpr] ... = ...
# 319| getDesugared: [StmtSequence] ...
# 319| getStmt: [SetterMethodCall] call to []=
# 319| getReceiver: [LocalVariableAccess] __synth__1
# 319| getArgument: [IntegerLiteral] 5
# 319| getArgument: [AssignExpr] ... = ...
# 319| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 319| getAnOperand/getRightOperand: [MethodCall] call to []
# 319| getReceiver: [LocalVariableAccess] __synth__2
# 319| getArgument: [RangeLiteral] _ .. _
# 319| getBegin: [IntegerLiteral] 1
# 319| getEnd: [IntegerLiteral] -1
# 319| getStmt: [LocalVariableAccess] __synth__0__1
# 319| getAnOperand/getLeftOperand: [MethodCall] call to []
# 320| [AssignAddExpr] ... += ...
# 320| getDesugared: [StmtSequence] ...
# 320| getStmt: [AssignExpr] ... = ...
# 320| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 320| getStmt: [SetterMethodCall] call to count=
# 320| getReceiver: [LocalVariableAccess] __synth__0
# 320| getArgument: [LocalVariableAccess] __synth__1
# 320| getAnOperand/getRightOperand: [SelfVariableAccess] self
# 320| getStmt: [AssignExpr] ... = ...
# 320| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 320| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 320| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to count
# 320| getReceiver: [LocalVariableAccess] __synth__0
# 320| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
# 320| getStmt: [SetterMethodCall] call to count=
# 320| getReceiver: [LocalVariableAccess] __synth__0
# 320| getArgument: [LocalVariableAccess] __synth__1
# 320| getStmt: [LocalVariableAccess] __synth__1
# 321| [AssignAddExpr] ... += ...
# 321| getDesugared: [StmtSequence] ...
# 321| getStmt: [AssignExpr] ... = ...
# 321| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 321| getAnOperand/getRightOperand: [MethodCall] call to foo
# 321| getReceiver: [SelfVariableAccess] self
# 321| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 321| getStmt: [SetterMethodCall] call to []=
# 321| getReceiver: [LocalVariableAccess] __synth__0
# 321| getArgument: [LocalVariableAccess] __synth__1
# 321| getArgument: [LocalVariableAccess] __synth__2
# 321| getStmt: [AssignExpr] ... = ...
# 321| getAnOperand/getRightOperand: [IntegerLiteral] 0
# 321| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 321| getAnOperand/getRightOperand: [IntegerLiteral] 0
# 321| getStmt: [AssignExpr] ... = ...
# 321| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 321| getAnOperand/getRightOperand: [AddExpr] ... + ...
@@ -205,35 +201,33 @@ calls/calls.rb:
# 321| getReceiver: [LocalVariableAccess] __synth__0
# 321| getArgument: [LocalVariableAccess] __synth__1
# 321| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
# 321| getStmt: [SetterMethodCall] call to []=
# 321| getReceiver: [LocalVariableAccess] __synth__0
# 321| getArgument: [LocalVariableAccess] __synth__1
# 321| getArgument: [LocalVariableAccess] __synth__2
# 321| getStmt: [LocalVariableAccess] __synth__2
# 322| [AssignMulExpr] ... *= ...
# 322| getDesugared: [StmtSequence] ...
# 322| getStmt: [AssignExpr] ... = ...
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 322| getAnOperand/getRightOperand: [MethodCall] call to bar
# 322| getReceiver: [MethodCall] call to foo
# 322| getReceiver: [SelfVariableAccess] self
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 322| getStmt: [SetterMethodCall] call to []=
# 322| getReceiver: [LocalVariableAccess] __synth__0
# 322| getArgument: [LocalVariableAccess] __synth__1
# 322| getArgument: [LocalVariableAccess] __synth__2
# 322| getArgument: [LocalVariableAccess] __synth__3
# 322| getArgument: [LocalVariableAccess] __synth__4
# 322| getStmt: [AssignExpr] ... = ...
# 322| getAnOperand/getRightOperand: [IntegerLiteral] 0
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__1
# 322| getAnOperand/getRightOperand: [IntegerLiteral] 0
# 322| getStmt: [AssignExpr] ... = ...
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 322| getAnOperand/getRightOperand: [MethodCall] call to baz
# 322| getReceiver: [MethodCall] call to foo
# 322| getReceiver: [SelfVariableAccess] self
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 322| getStmt: [AssignExpr] ... = ...
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 322| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 322| getAnOperand/getLeftOperand/getReceiver: [MethodCall] call to boo
# 322| getReceiver: [MethodCall] call to foo
# 322| getReceiver: [SelfVariableAccess] self
# 322| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 1
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 322| getStmt: [AssignExpr] ... = ...
# 322| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__4
# 322| getAnOperand/getRightOperand: [MulExpr] ... * ...
@@ -243,9 +237,30 @@ calls/calls.rb:
# 322| getArgument: [LocalVariableAccess] __synth__2
# 322| getArgument: [LocalVariableAccess] __synth__3
# 322| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 2
# 322| getStmt: [SetterMethodCall] call to []=
# 322| getReceiver: [LocalVariableAccess] __synth__0
# 322| getArgument: [LocalVariableAccess] __synth__1
# 322| getArgument: [LocalVariableAccess] __synth__2
# 322| getArgument: [LocalVariableAccess] __synth__3
# 322| getArgument: [LocalVariableAccess] __synth__4
# 322| getStmt: [LocalVariableAccess] __synth__4
# 342| [ForExpr] for ... in ...
# 342| getDesugared: [MethodCall] call to each
# 342| getReceiver: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 1
# 342| getArgument: [IntegerLiteral] 2
# 342| getArgument: [IntegerLiteral] 3
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 4
# 342| getArgument: [IntegerLiteral] 5
# 342| getArgument: [IntegerLiteral] 6
# 342| getBlock: [BraceBlock] { ... }
# 342| getParameter: [SimpleParameter] __synth__0__1
# 342| getDefiningAccess: [LocalVariableAccess] __synth__0__1
@@ -276,41 +291,29 @@ calls/calls.rb:
# 343| getArgument: [LocalVariableAccess] x
# 343| getArgument: [LocalVariableAccess] y
# 343| getArgument: [LocalVariableAccess] z
# 342| getReceiver: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 1
# 342| getArgument: [IntegerLiteral] 2
# 342| getArgument: [IntegerLiteral] 3
# 342| getArgument: [ArrayLiteral] [...]
# 342| getDesugared: [MethodCall] call to []
# 342| getReceiver: [ConstantReadAccess] Array
# 342| getArgument: [IntegerLiteral] 4
# 342| getArgument: [IntegerLiteral] 5
# 342| getArgument: [IntegerLiteral] 6
# 364| [MethodCall] call to empty?
# 364| getDesugared: [StmtSequence] ...
# 364| getStmt: [AssignExpr] ... = ...
# 364| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 364| getAnOperand/getRightOperand: [MethodCall] call to list
# 364| getReceiver: [SelfVariableAccess] self
# 364| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 364| getStmt: [IfExpr] if ...
# 364| getCondition: [MethodCall] call to ==
# 364| getReceiver: [NilLiteral] nil
# 364| getArgument: [LocalVariableAccess] __synth__0__1
# 364| getBranch/getThen: [NilLiteral] nil
# 364| getBranch/getElse: [MethodCall] call to empty?
# 364| getReceiver: [LocalVariableAccess] __synth__0__1
# 364| getCondition: [MethodCall] call to ==
# 364| getArgument: [LocalVariableAccess] __synth__0__1
# 364| getReceiver: [NilLiteral] nil
# 366| [MethodCall] call to bar
# 366| getDesugared: [StmtSequence] ...
# 366| getStmt: [AssignExpr] ... = ...
# 366| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 366| getAnOperand/getRightOperand: [MethodCall] call to foo
# 366| getReceiver: [SelfVariableAccess] self
# 366| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0__1
# 366| getStmt: [IfExpr] if ...
# 366| getCondition: [MethodCall] call to ==
# 366| getReceiver: [NilLiteral] nil
# 366| getArgument: [LocalVariableAccess] __synth__0__1
# 366| getBranch/getThen: [NilLiteral] nil
# 366| getBranch/getElse: [MethodCall] call to bar
# 366| getReceiver: [LocalVariableAccess] __synth__0__1
@@ -320,9 +323,6 @@ calls/calls.rb:
# 366| getParameter: [SimpleParameter] x
# 366| getDefiningAccess: [LocalVariableAccess] x
# 366| getStmt: [LocalVariableAccess] x
# 366| getCondition: [MethodCall] call to ==
# 366| getArgument: [LocalVariableAccess] __synth__0__1
# 366| getReceiver: [NilLiteral] nil
control/cases.rb:
# 90| [ArrayLiteral] %w(...)
# 90| getDesugared: [MethodCall] call to []
@@ -343,10 +343,10 @@ control/cases.rb:
# 160| getValue: [MethodCall] call to expr
# 160| getReceiver: [SelfVariableAccess] self
# 160| getBranch: [InClause] in ... then ...
# 160| getBody: [BooleanLiteral] true
# 160| getPattern: [ArrayPattern] [ ..., * ]
# 160| getPrefixElement: [IntegerLiteral] 1
# 160| getPrefixElement: [IntegerLiteral] 2
# 160| getBody: [BooleanLiteral] true
# 160| getBranch/getElseBranch: [StmtSequence] else ...
# 160| getStmt: [BooleanLiteral] false
# 162| [MatchPattern] ... => ...
@@ -354,7 +354,6 @@ control/cases.rb:
# 162| getValue: [MethodCall] call to expr
# 162| getReceiver: [SelfVariableAccess] self
# 162| getBranch: [InClause] in ... then ...
# 162| getBody: [NilLiteral] nil
# 162| getPattern: [HashPattern] { ..., ** }
# 162| getKey: [SymbolLiteral] :x
# 162| getComponent: [StringTextComponent] x
@@ -362,6 +361,7 @@ control/cases.rb:
# 162| getKey: [SymbolLiteral] :y
# 162| getComponent: [StringTextComponent] y
# 162| getValue: [IntegerLiteral] 1
# 162| getBody: [NilLiteral] nil
constants/constants.rb:
# 20| [ArrayLiteral] [...]
# 20| getDesugared: [MethodCall] call to []
@@ -595,12 +595,15 @@ literals/literals.rb:
control/loops.rb:
# 9| [ForExpr] for ... in ...
# 9| getDesugared: [MethodCall] call to each
# 9| getReceiver: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
# 9| getBlock: [BraceBlock] { ... }
# 9| getParameter: [SimpleParameter] __synth__0__1
# 9| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 9| getStmt: [AssignExpr] ... = ...
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 9| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 10| getStmt: [AssignAddExpr] ... += ...
# 10| getDesugared: [AssignExpr] ... = ...
# 10| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
@@ -610,17 +613,17 @@ control/loops.rb:
# 11| getStmt: [AssignExpr] ... = ...
# 11| getAnOperand/getLeftOperand: [LocalVariableAccess] foo
# 11| getAnOperand/getRightOperand: [LocalVariableAccess] n
# 9| getReceiver: [RangeLiteral] _ .. _
# 9| getBegin: [IntegerLiteral] 1
# 9| getEnd: [IntegerLiteral] 10
# 16| [ForExpr] for ... in ...
# 16| getDesugared: [MethodCall] call to each
# 16| getReceiver: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
# 16| getBlock: [BraceBlock] { ... }
# 16| getParameter: [SimpleParameter] __synth__0__1
# 16| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 16| getStmt: [AssignExpr] ... = ...
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 16| getAnOperand/getLeftOperand: [LocalVariableAccess] n
# 16| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 17| getStmt: [AssignAddExpr] ... += ...
# 17| getDesugared: [AssignExpr] ... = ...
# 17| getAnOperand/getLeftOperand: [LocalVariableAccess] sum
@@ -633,11 +636,19 @@ control/loops.rb:
# 18| getAnOperand/getRightOperand: [SubExpr] ... - ...
# 18| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 18| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] n
# 16| getReceiver: [RangeLiteral] _ .. _
# 16| getBegin: [IntegerLiteral] 1
# 16| getEnd: [IntegerLiteral] 10
# 22| [ForExpr] for ... in ...
# 22| getDesugared: [MethodCall] call to each
# 22| getReceiver: [HashLiteral] {...}
# 22| getDesugared: [MethodCall] call to []
# 22| getReceiver: [ConstantReadAccess] Hash
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getComponent: [StringTextComponent] foo
# 22| getValue: [IntegerLiteral] 0
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getComponent: [StringTextComponent] bar
# 22| getValue: [IntegerLiteral] 1
# 22| getBlock: [BraceBlock] { ... }
# 22| getParameter: [SimpleParameter] __synth__0__1
# 22| getDefiningAccess: [LocalVariableAccess] __synth__0__1
@@ -670,19 +681,19 @@ control/loops.rb:
# 24| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 24| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 24| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 22| getReceiver: [HashLiteral] {...}
# 22| getDesugared: [MethodCall] call to []
# 22| getReceiver: [ConstantReadAccess] Hash
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :foo
# 22| getComponent: [StringTextComponent] foo
# 22| getValue: [IntegerLiteral] 0
# 22| getArgument: [Pair] Pair
# 22| getKey: [SymbolLiteral] :bar
# 22| getComponent: [StringTextComponent] bar
# 22| getValue: [IntegerLiteral] 1
# 28| [ForExpr] for ... in ...
# 28| getDesugared: [MethodCall] call to each
# 28| getReceiver: [HashLiteral] {...}
# 28| getDesugared: [MethodCall] call to []
# 28| getReceiver: [ConstantReadAccess] Hash
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getComponent: [StringTextComponent] foo
# 28| getValue: [IntegerLiteral] 0
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getComponent: [StringTextComponent] bar
# 28| getValue: [IntegerLiteral] 1
# 28| getBlock: [BraceBlock] { ... }
# 28| getParameter: [SimpleParameter] __synth__0__1
# 28| getDefiningAccess: [LocalVariableAccess] __synth__0__1
@@ -716,17 +727,6 @@ control/loops.rb:
# 30| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] foo
# 30| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] value
# 31| getStmt: [BreakStmt] break
# 28| getReceiver: [HashLiteral] {...}
# 28| getDesugared: [MethodCall] call to []
# 28| getReceiver: [ConstantReadAccess] Hash
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :foo
# 28| getComponent: [StringTextComponent] foo
# 28| getValue: [IntegerLiteral] 0
# 28| getArgument: [Pair] Pair
# 28| getKey: [SymbolLiteral] :bar
# 28| getComponent: [StringTextComponent] bar
# 28| getValue: [IntegerLiteral] 1
# 36| [AssignAddExpr] ... += ...
# 36| getDesugared: [AssignExpr] ... = ...
# 36| getAnOperand/getLeftOperand: [LocalVariableAccess] x
@@ -916,8 +916,8 @@ operations/operations.rb:
# 101| [AssignLogicalOrExpr] ... ||= ...
# 101| getDesugared: [StmtSequence] ...
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo
# 101| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getLeftOperand: [ConstantAssignment] MemberConstant
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
@@ -928,11 +928,11 @@ operations/operations.rb:
# 102| [AssignLogicalOrExpr] ... ||= ...
# 102| getDesugared: [StmtSequence] ...
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 102| getAnOperand/getRightOperand: [MethodCall] call to bar
# 102| getReceiver: [MethodCall] call to foo
# 102| getReceiver: [SelfVariableAccess] self
# 102| getArgument: [IntegerLiteral] 1
# 102| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getLeftOperand: [ConstantAssignment] OtherConstant
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
@@ -949,6 +949,18 @@ operations/operations.rb:
# 104| [AssignExpr] ... = ...
# 104| getDesugared: [StmtSequence] ...
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 104| getAnOperand/getRightOperand: [LocalVariableAccess] foo
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 104| getAnOperand/getRightOperand: [SplatExpr] * ...
# 104| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...]
# 104| getDesugared: [MethodCall] call to []
# 104| getReceiver: [ConstantReadAccess] Array
# 104| getArgument: [IntegerLiteral] 1
# 104| getArgument: [IntegerLiteral] 2
# 104| getArgument: [IntegerLiteral] 3
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getLeftOperand: [ConstantAssignment] FOO
# 104| getAnOperand/getRightOperand: [MethodCall] call to []
# 104| getReceiver: [LocalVariableAccess] __synth__3
@@ -959,23 +971,11 @@ operations/operations.rb:
# 104| getReceiver: [LocalVariableAccess] __synth__3
# 104| getArgument: [IntegerLiteral] 1
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getRightOperand: [LocalVariableAccess] foo
# 104| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__2
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getLeftOperand: [ConstantAssignment] FOO
# 104| getScopeExpr: [LocalVariableAccess] __synth__2
# 104| getAnOperand/getRightOperand: [MethodCall] call to []
# 104| getReceiver: [LocalVariableAccess] __synth__3
# 104| getArgument: [IntegerLiteral] 2
# 104| getStmt: [AssignExpr] ... = ...
# 104| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__3
# 104| getAnOperand/getRightOperand: [SplatExpr] * ...
# 104| getAnOperand/getOperand/getReceiver: [ArrayLiteral] [...]
# 104| getDesugared: [MethodCall] call to []
# 104| getReceiver: [ConstantReadAccess] Array
# 104| getArgument: [IntegerLiteral] 1
# 104| getArgument: [IntegerLiteral] 2
# 104| getArgument: [IntegerLiteral] 3
params/params.rb:
# 8| [HashLiteral] {...}
# 8| getDesugared: [MethodCall] call to []
@@ -986,19 +986,6 @@ params/params.rb:
erb/template.html.erb:
# 27| [ForExpr] for ... in ...
# 27| getDesugared: [MethodCall] call to each
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
# 27| getReceiver: [ArrayLiteral] [...]
# 27| getDesugared: [MethodCall] call to []
# 27| getReceiver: [ConstantReadAccess] Array
@@ -1008,82 +995,95 @@ erb/template.html.erb:
# 27| getComponent: [StringTextComponent] bar
# 27| getArgument: [StringLiteral] "baz"
# 27| getComponent: [StringTextComponent] baz
# 27| getBlock: [BraceBlock] { ... }
# 27| getParameter: [SimpleParameter] __synth__0__1
# 27| getDefiningAccess: [LocalVariableAccess] __synth__0__1
# 27| getStmt: [AssignExpr] ... = ...
# 27| getAnOperand/getLeftOperand: [LocalVariableAccess] x
# 27| getAnOperand/getRightOperand: [LocalVariableAccess] __synth__0__1
# 28| getStmt: [AssignAddExpr] ... += ...
# 28| getDesugared: [AssignExpr] ... = ...
# 28| getAnOperand/getLeftOperand: [LocalVariableAccess] xs
# 28| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 28| getAnOperand/getLeftOperand/getReceiver: [LocalVariableAccess] xs
# 28| getAnOperand/getArgument/getRightOperand: [LocalVariableAccess] x
# 29| getStmt: [LocalVariableAccess] xs
gems/test.gemspec:
# 2| [AssignExpr] ... = ...
# 2| getDesugared: [StmtSequence] ...
# 2| getStmt: [SetterMethodCall] call to name=
# 2| getReceiver: [LocalVariableAccess] s
# 2| getArgument: [AssignExpr] ... = ...
# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 2| getAnOperand/getRightOperand: [StringLiteral] "test"
# 2| getComponent: [StringTextComponent] test
# 2| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 2| getStmt: [LocalVariableAccess] __synth__0
# 3| [AssignExpr] ... = ...
# 3| getDesugared: [StmtSequence] ...
# 3| getStmt: [SetterMethodCall] call to version=
# 3| getReceiver: [LocalVariableAccess] s
# 3| getArgument: [AssignExpr] ... = ...
# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 3| getAnOperand/getRightOperand: [StringLiteral] "0.0.0"
# 3| getComponent: [StringTextComponent] 0.0.0
# 3| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 3| getStmt: [LocalVariableAccess] __synth__0
# 4| [AssignExpr] ... = ...
# 4| getDesugared: [StmtSequence] ...
# 4| getStmt: [SetterMethodCall] call to summary=
# 4| getReceiver: [LocalVariableAccess] s
# 4| getArgument: [AssignExpr] ... = ...
# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 4| getAnOperand/getRightOperand: [StringLiteral] "foo!"
# 4| getComponent: [StringTextComponent] foo!
# 4| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 4| getStmt: [LocalVariableAccess] __synth__0
# 5| [AssignExpr] ... = ...
# 5| getDesugared: [StmtSequence] ...
# 5| getStmt: [SetterMethodCall] call to description=
# 5| getReceiver: [LocalVariableAccess] s
# 5| getArgument: [AssignExpr] ... = ...
# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 5| getAnOperand/getRightOperand: [StringLiteral] "A test"
# 5| getComponent: [StringTextComponent] A test
# 5| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 5| getStmt: [LocalVariableAccess] __synth__0
# 6| [AssignExpr] ... = ...
# 6| getDesugared: [StmtSequence] ...
# 6| getStmt: [SetterMethodCall] call to authors=
# 6| getReceiver: [LocalVariableAccess] s
# 6| getArgument: [AssignExpr] ... = ...
# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 6| getAnOperand/getRightOperand: [ArrayLiteral] [...]
# 6| getDesugared: [MethodCall] call to []
# 6| getReceiver: [ConstantReadAccess] Array
# 6| getArgument: [StringLiteral] "Mona Lisa"
# 6| getComponent: [StringTextComponent] Mona Lisa
# 6| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 6| getStmt: [LocalVariableAccess] __synth__0
# 7| [AssignExpr] ... = ...
# 7| getDesugared: [StmtSequence] ...
# 7| getStmt: [SetterMethodCall] call to email=
# 7| getReceiver: [LocalVariableAccess] s
# 7| getArgument: [AssignExpr] ... = ...
# 7| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 7| getAnOperand/getRightOperand: [StringLiteral] "mona@example.com"
# 7| getComponent: [StringTextComponent] mona@example.com
# 7| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 7| getStmt: [LocalVariableAccess] __synth__0
# 8| [AssignExpr] ... = ...
# 8| getDesugared: [StmtSequence] ...
# 8| getStmt: [SetterMethodCall] call to files=
# 8| getReceiver: [LocalVariableAccess] s
# 8| getArgument: [AssignExpr] ... = ...
# 8| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 8| getAnOperand/getRightOperand: [ArrayLiteral] [...]
# 8| getDesugared: [MethodCall] call to []
# 8| getReceiver: [ConstantReadAccess] Array
# 8| getArgument: [StringLiteral] "lib/test.rb"
# 8| getComponent: [StringTextComponent] lib/test.rb
# 8| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 8| getStmt: [LocalVariableAccess] __synth__0
# 9| [AssignExpr] ... = ...
# 9| getDesugared: [StmtSequence] ...
# 9| getStmt: [SetterMethodCall] call to homepage=
# 9| getReceiver: [LocalVariableAccess] s
# 9| getArgument: [AssignExpr] ... = ...
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 9| getAnOperand/getRightOperand: [StringLiteral] "https://github.com/github/cod..."
# 9| getComponent: [StringTextComponent] https://github.com/github/codeql-ruby
# 9| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 9| getStmt: [LocalVariableAccess] __synth__0

View File

@@ -77,12 +77,12 @@ callsWithArguments
| calls.rb:319:14:319:22 | call to [] | [] | 0 | calls.rb:319:15:319:15 | 1 |
| calls.rb:319:14:319:22 | call to [] | [] | 1 | calls.rb:319:18:319:18 | 2 |
| calls.rb:319:14:319:22 | call to [] | [] | 2 | calls.rb:319:21:319:21 | 3 |
| calls.rb:320:1:320:10 | call to count= | count= | 1 | calls.rb:320:12:320:13 | __synth__1 |
| calls.rb:320:1:320:10 | call to count= | count= | 0 | calls.rb:320:1:320:10 | __synth__1 |
| calls.rb:320:12:320:13 | ... + ... | + | 0 | calls.rb:320:15:320:15 | 1 |
| calls.rb:321:1:321:6 | ...[...] | [] | 0 | calls.rb:321:5:321:5 | 0 |
| calls.rb:321:1:321:6 | call to [] | [] | 0 | calls.rb:321:5:321:5 | __synth__1 |
| calls.rb:321:1:321:6 | call to []= | []= | 0 | calls.rb:321:5:321:5 | __synth__1 |
| calls.rb:321:1:321:6 | call to []= | []= | 2 | calls.rb:321:8:321:9 | __synth__2 |
| calls.rb:321:1:321:6 | call to []= | []= | 1 | calls.rb:321:1:321:6 | __synth__2 |
| calls.rb:321:8:321:9 | ... + ... | + | 0 | calls.rb:321:11:321:11 | 1 |
| calls.rb:322:1:322:32 | ...[...] | [] | 0 | calls.rb:322:9:322:9 | 0 |
| calls.rb:322:1:322:32 | ...[...] | [] | 1 | calls.rb:322:12:322:18 | call to baz |
@@ -93,7 +93,7 @@ callsWithArguments
| calls.rb:322:1:322:32 | call to []= | []= | 0 | calls.rb:322:9:322:9 | __synth__1 |
| calls.rb:322:1:322:32 | call to []= | []= | 1 | calls.rb:322:12:322:18 | __synth__2 |
| calls.rb:322:1:322:32 | call to []= | []= | 2 | calls.rb:322:21:322:31 | __synth__3 |
| calls.rb:322:1:322:32 | call to []= | []= | 4 | calls.rb:322:34:322:35 | __synth__4 |
| calls.rb:322:1:322:32 | call to []= | []= | 3 | calls.rb:322:1:322:32 | __synth__4 |
| calls.rb:322:21:322:31 | ... + ... | + | 0 | calls.rb:322:31:322:31 | 1 |
| calls.rb:322:34:322:35 | ... * ... | * | 0 | calls.rb:322:37:322:37 | 2 |
| calls.rb:330:25:330:37 | call to print | print | 0 | calls.rb:330:31:330:37 | "error" |

View File

@@ -4004,6 +4004,9 @@ desugar.rb:
# 14| call to foo
#-----| -> ... = ...
# 14| __synth__1
#-----| -> call to count=
# 14| call to count
#-----| -> 1
@@ -4025,9 +4028,6 @@ desugar.rb:
# 14| __synth__1
#-----| -> __synth__0
# 14| __synth__1
#-----| -> call to count=
# 14| 1
#-----| -> ... + ...
@@ -4066,6 +4066,9 @@ desugar.rb:
# 18| call to foo
#-----| -> ... = ...
# 18| __synth__4
#-----| -> call to []=
# 18| call to []
#-----| -> 1
@@ -4144,9 +4147,6 @@ desugar.rb:
# 18| __synth__4
#-----| -> __synth__0
# 18| __synth__4
#-----| -> call to []=
# 18| 1
#-----| -> ... + ...

View File

@@ -219,15 +219,15 @@ positionalArguments
| desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:14:10:14 | ... = ... |
| desugar.rb:14:3:14:13 | call to count= | desugar.rb:14:15:14:16 | __synth__1 |
| desugar.rb:14:3:14:13 | call to count= | desugar.rb:14:3:14:13 | __synth__1 |
| desugar.rb:14:15:14:16 | ... + ... | desugar.rb:14:18:14:18 | 1 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:9:18:9 | __synth__1 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:12:18:16 | __synth__2 |
| desugar.rb:18:3:18:28 | call to [] | desugar.rb:18:19:18:27 | __synth__3 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:3:18:28 | __synth__4 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:9:18:9 | __synth__1 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:12:18:16 | __synth__2 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:19:18:27 | __synth__3 |
| desugar.rb:18:3:18:28 | call to []= | desugar.rb:18:30:18:31 | __synth__4 |
| desugar.rb:18:19:18:27 | ... + ... | desugar.rb:18:27:18:27 | 3 |
| desugar.rb:18:30:18:31 | ... + ... | desugar.rb:18:33:18:33 | 1 |
| desugar.rb:22:3:22:3 | call to [] | desugar.rb:22:3:22:3 | 0 |

View File

@@ -36,6 +36,12 @@
#-----| TrueClass
#-----| super -> Object
#-----| UnresolvedNamespace::X1
#-----| UnresolvedNamespace::X1::X2
#-----| UnresolvedNamespace::X1::X2::X3
calls.rb:
# 21| M
@@ -272,3 +278,12 @@ unresolved_subclass.rb:
# 11| UnresolvedNamespace::A
#-----| super -> Object
# 14| UnresolvedNamespace::X1::X2::X3::Subclass1
#-----| super -> ResolvableBaseClass
# 17| UnresolvedNamespace::X1::X2::X3::Subclass2
#-----| super -> UnresolvedNamespace::X1::X2::X3::Subclass1
# 21| UnresolvedNamespace::X1::X2::X3::A
#-----| super -> Object

View File

@@ -660,6 +660,15 @@ lookupMethod
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A | new | calls.rb:117:5:117:16 | new |
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A | puts | calls.rb:102:5:102:30 | puts |
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A | to_s | calls.rb:172:5:173:7 | to_s |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 | new | calls.rb:117:5:117:16 | new |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 | puts | calls.rb:102:5:102:30 | puts |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 | to_s | calls.rb:172:5:173:7 | to_s |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 | new | calls.rb:117:5:117:16 | new |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 | puts | calls.rb:102:5:102:30 | puts |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 | to_s | calls.rb:172:5:173:7 | to_s |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A | new | calls.rb:117:5:117:16 | new |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A | puts | calls.rb:102:5:102:30 | puts |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A | to_s | calls.rb:172:5:173:7 | to_s |
enclosingMethod
| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:3:3 | foo |
| calls.rb:2:5:2:14 | self | calls.rb:1:1:3:3 | foo |

View File

@@ -51,6 +51,9 @@ getModule
| file://:0:0:0:0 | Symbol |
| file://:0:0:0:0 | TrueClass |
| file://:0:0:0:0 | UnresolvedNamespace |
| file://:0:0:0:0 | UnresolvedNamespace::X1 |
| file://:0:0:0:0 | UnresolvedNamespace::X1::X2 |
| file://:0:0:0:0 | UnresolvedNamespace::X1::X2::X3 |
| hello.rb:1:1:8:3 | EnglishWords |
| hello.rb:11:1:16:3 | Greeting |
| hello.rb:18:1:22:3 | HelloWorld |
@@ -97,6 +100,9 @@ getModule
| unresolved_subclass.rb:4:1:5:3 | UnresolvedNamespace::Subclass1 |
| unresolved_subclass.rb:7:1:8:3 | UnresolvedNamespace::Subclass2 |
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A |
getADeclaration
| calls.rb:21:1:34:3 | M | calls.rb:21:1:34:3 | M |
| calls.rb:43:1:58:3 | C | calls.rb:43:1:58:3 | C |
@@ -113,7 +119,7 @@ getADeclaration
| calls.rb:115:1:118:3 | Object | modules_rec.rb:1:1:11:26 | modules_rec.rb |
| calls.rb:115:1:118:3 | Object | private.rb:1:1:105:40 | private.rb |
| calls.rb:115:1:118:3 | Object | toplevel_self_singleton.rb:1:1:34:4 | toplevel_self_singleton.rb |
| calls.rb:115:1:118:3 | Object | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| calls.rb:115:1:118:3 | Object | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| calls.rb:120:1:123:3 | Hash | calls.rb:120:1:123:3 | Hash |
| calls.rb:125:1:138:3 | Array | calls.rb:125:1:138:3 | Array |
| calls.rb:165:1:169:3 | S | calls.rb:165:1:169:3 | S |
@@ -197,6 +203,9 @@ getADeclaration
| unresolved_subclass.rb:4:1:5:3 | UnresolvedNamespace::Subclass1 | unresolved_subclass.rb:4:1:5:3 | Subclass1 |
| unresolved_subclass.rb:7:1:8:3 | UnresolvedNamespace::Subclass2 | unresolved_subclass.rb:7:1:8:3 | Subclass2 |
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A | unresolved_subclass.rb:11:1:12:3 | A |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 | unresolved_subclass.rb:14:1:15:3 | Subclass1 |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 | unresolved_subclass.rb:17:1:18:3 | Subclass2 |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A | unresolved_subclass.rb:21:1:22:3 | A |
getSuperClass
| calls.rb:43:1:58:3 | C | calls.rb:115:1:118:3 | Object |
| calls.rb:65:1:69:3 | D | calls.rb:43:1:58:3 | C |
@@ -259,6 +268,9 @@ getSuperClass
| unresolved_subclass.rb:4:1:5:3 | UnresolvedNamespace::Subclass1 | unresolved_subclass.rb:1:1:2:3 | ResolvableBaseClass |
| unresolved_subclass.rb:7:1:8:3 | UnresolvedNamespace::Subclass2 | unresolved_subclass.rb:4:1:5:3 | UnresolvedNamespace::Subclass1 |
| unresolved_subclass.rb:11:1:12:3 | UnresolvedNamespace::A | calls.rb:115:1:118:3 | Object |
| unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 | unresolved_subclass.rb:1:1:2:3 | ResolvableBaseClass |
| unresolved_subclass.rb:17:1:18:3 | UnresolvedNamespace::X1::X2::X3::Subclass2 | unresolved_subclass.rb:14:1:15:3 | UnresolvedNamespace::X1::X2::X3::Subclass1 |
| unresolved_subclass.rb:21:1:22:3 | UnresolvedNamespace::X1::X2::X3::A | calls.rb:115:1:118:3 | Object |
getAPrependedModule
| calls.rb:115:1:118:3 | Object | calls.rb:171:1:174:3 | A |
| calls.rb:171:1:174:3 | A | toplevel_self_singleton.rb:2:5:5:7 | A::B |
@@ -419,6 +431,28 @@ resolveConstantReadAccess
| unresolved_subclass.rb:7:40:7:69 | Subclass1 | UnresolvedNamespace::Subclass1 |
| unresolved_subclass.rb:11:7:11:25 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:11:32:11:50 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:14:7:14:25 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:14:7:14:29 | X1 | UnresolvedNamespace::X1 |
| unresolved_subclass.rb:14:7:14:33 | X2 | UnresolvedNamespace::X1::X2 |
| unresolved_subclass.rb:14:7:14:37 | X3 | UnresolvedNamespace::X1::X2::X3 |
| unresolved_subclass.rb:14:52:14:70 | ResolvableBaseClass | ResolvableBaseClass |
| unresolved_subclass.rb:17:7:17:25 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:17:7:17:29 | X1 | UnresolvedNamespace::X1 |
| unresolved_subclass.rb:17:7:17:33 | X2 | UnresolvedNamespace::X1::X2 |
| unresolved_subclass.rb:17:7:17:37 | X3 | UnresolvedNamespace::X1::X2::X3 |
| unresolved_subclass.rb:17:52:17:70 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:17:52:17:74 | X1 | UnresolvedNamespace::X1 |
| unresolved_subclass.rb:17:52:17:78 | X2 | UnresolvedNamespace::X1::X2 |
| unresolved_subclass.rb:17:52:17:82 | X3 | UnresolvedNamespace::X1::X2::X3 |
| unresolved_subclass.rb:17:52:17:93 | Subclass1 | UnresolvedNamespace::X1::X2::X3::Subclass1 |
| unresolved_subclass.rb:21:7:21:25 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:21:7:21:29 | X1 | UnresolvedNamespace::X1 |
| unresolved_subclass.rb:21:7:21:33 | X2 | UnresolvedNamespace::X1::X2 |
| unresolved_subclass.rb:21:7:21:37 | X3 | UnresolvedNamespace::X1::X2::X3 |
| unresolved_subclass.rb:21:44:21:62 | UnresolvedNamespace | UnresolvedNamespace |
| unresolved_subclass.rb:21:44:21:66 | X1 | UnresolvedNamespace::X1 |
| unresolved_subclass.rb:21:44:21:70 | X2 | UnresolvedNamespace::X1::X2 |
| unresolved_subclass.rb:21:44:21:74 | X3 | UnresolvedNamespace::X1::X2::X3 |
resolveConstantWriteAccess
| calls.rb:21:1:34:3 | M | M |
| calls.rb:43:1:58:3 | C | C |
@@ -523,6 +557,9 @@ resolveConstantWriteAccess
| unresolved_subclass.rb:4:1:5:3 | Subclass1 | UnresolvedNamespace::Subclass1 |
| unresolved_subclass.rb:7:1:8:3 | Subclass2 | UnresolvedNamespace::Subclass2 |
| unresolved_subclass.rb:11:1:12:3 | A | UnresolvedNamespace::A |
| unresolved_subclass.rb:14:1:15:3 | Subclass1 | UnresolvedNamespace::X1::X2::X3::Subclass1 |
| unresolved_subclass.rb:17:1:18:3 | Subclass2 | UnresolvedNamespace::X1::X2::X3::Subclass2 |
| unresolved_subclass.rb:21:1:22:3 | A | UnresolvedNamespace::X1::X2::X3::A |
enclosingModule
| calls.rb:1:1:3:3 | foo | calls.rb:1:1:651:24 | calls.rb |
| calls.rb:2:5:2:14 | call to puts | calls.rb:1:1:651:24 | calls.rb |
@@ -1878,15 +1915,41 @@ enclosingModule
| toplevel_self_singleton.rb:30:13:30:19 | self | toplevel_self_singleton.rb:25:5:33:7 | class << ... |
| toplevel_self_singleton.rb:31:13:31:20 | call to call_you | toplevel_self_singleton.rb:25:5:33:7 | class << ... |
| toplevel_self_singleton.rb:31:13:31:20 | self | toplevel_self_singleton.rb:25:5:33:7 | class << ... |
| unresolved_subclass.rb:1:1:2:3 | ResolvableBaseClass | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:1:5:3 | Subclass1 | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:7:4:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:40:4:58 | ResolvableBaseClass | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:1:8:3 | Subclass2 | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:7:7:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:40:7:58 | UnresolvedNamespace | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:40:7:69 | Subclass1 | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:1:12:3 | A | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:7:11:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:32:11:50 | UnresolvedNamespace | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:32:11:53 | B | unresolved_subclass.rb:1:1:12:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:1:1:2:3 | ResolvableBaseClass | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:1:5:3 | Subclass1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:7:4:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:4:40:4:58 | ResolvableBaseClass | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:1:8:3 | Subclass2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:7:7:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:40:7:58 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:7:40:7:69 | Subclass1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:1:12:3 | A | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:7:11:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:32:11:50 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:11:32:11:53 | B | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:1:15:3 | Subclass1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:7:14:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:7:14:29 | X1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:7:14:33 | X2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:7:14:37 | X3 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:14:52:14:70 | ResolvableBaseClass | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:1:18:3 | Subclass2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:7:17:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:7:17:29 | X1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:7:17:33 | X2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:7:17:37 | X3 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:52:17:70 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:52:17:74 | X1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:52:17:78 | X2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:52:17:82 | X3 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:17:52:17:93 | Subclass1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:1:22:3 | A | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:7:21:25 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:7:21:29 | X1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:7:21:33 | X2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:7:21:37 | X3 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:44:21:62 | UnresolvedNamespace | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:44:21:66 | X1 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:44:21:70 | X2 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:44:21:74 | X3 | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |
| unresolved_subclass.rb:21:44:21:77 | B | unresolved_subclass.rb:1:1:22:4 | unresolved_subclass.rb |

View File

@@ -36,6 +36,12 @@
#-----| TrueClass
#-----| -> Object
#-----| UnresolvedNamespace::X1
#-----| UnresolvedNamespace::X1::X2
#-----| UnresolvedNamespace::X1::X2::X3
calls.rb:
# 21| M
@@ -262,3 +268,12 @@ unresolved_subclass.rb:
# 11| UnresolvedNamespace::A
#-----| -> Object
# 14| UnresolvedNamespace::X1::X2::X3::Subclass1
#-----| -> ResolvableBaseClass
# 17| UnresolvedNamespace::X1::X2::X3::Subclass2
#-----| -> UnresolvedNamespace::X1::X2::X3::Subclass1
# 21| UnresolvedNamespace::X1::X2::X3::A
#-----| -> Object

View File

@@ -10,3 +10,13 @@ end
# Ensure Object is a transitive superclass of this
class UnresolvedNamespace::A < UnresolvedNamespace::B
end
class UnresolvedNamespace::X1::X2::X3::Subclass1 < ResolvableBaseClass
end
class UnresolvedNamespace::X1::X2::X3::Subclass2 < UnresolvedNamespace::X1::X2::X3::Subclass1
end
# Ensure Object is a transitive superclass of this
class UnresolvedNamespace::X1::X2::X3::A < UnresolvedNamespace::X1::X2::X3::B
end

View File

@@ -1,26 +1,3 @@
edges
| app/controllers/users_controller.rb:4:11:4:16 | call to params : | app/controllers/users_controller.rb:4:11:4:27 | ...[...] |
| app/controllers/users_controller.rb:9:5:9:12 | password : | app/controllers/users_controller.rb:10:42:10:49 | password |
| app/controllers/users_controller.rb:9:16:9:21 | call to params : | app/controllers/users_controller.rb:9:16:9:27 | ...[...] : |
| app/controllers/users_controller.rb:9:16:9:27 | ...[...] : | app/controllers/users_controller.rb:9:5:9:12 | password : |
| app/controllers/users_controller.rb:14:5:14:13 | [post] self [@password] : | app/controllers/users_controller.rb:15:42:15:50 | self [@password] : |
| app/controllers/users_controller.rb:14:17:14:22 | call to params : | app/controllers/users_controller.rb:14:17:14:28 | ...[...] : |
| app/controllers/users_controller.rb:14:17:14:28 | ...[...] : | app/controllers/users_controller.rb:14:5:14:13 | [post] self [@password] : |
| app/controllers/users_controller.rb:15:42:15:50 | self [@password] : | app/controllers/users_controller.rb:15:42:15:50 | @password |
nodes
| app/controllers/users_controller.rb:4:11:4:16 | call to params : | semmle.label | call to params : |
| app/controllers/users_controller.rb:4:11:4:27 | ...[...] | semmle.label | ...[...] |
| app/controllers/users_controller.rb:9:5:9:12 | password : | semmle.label | password : |
| app/controllers/users_controller.rb:9:16:9:21 | call to params : | semmle.label | call to params : |
| app/controllers/users_controller.rb:9:16:9:27 | ...[...] : | semmle.label | ...[...] : |
| app/controllers/users_controller.rb:10:42:10:49 | password | semmle.label | password |
| app/controllers/users_controller.rb:14:5:14:13 | [post] self [@password] : | semmle.label | [post] self [@password] : |
| app/controllers/users_controller.rb:14:17:14:22 | call to params : | semmle.label | call to params : |
| app/controllers/users_controller.rb:14:17:14:28 | ...[...] : | semmle.label | ...[...] : |
| app/controllers/users_controller.rb:15:42:15:50 | @password | semmle.label | @password |
| app/controllers/users_controller.rb:15:42:15:50 | self [@password] : | semmle.label | self [@password] : |
subpaths
#select
| app/controllers/users_controller.rb:4:11:4:16 | call to params | app/controllers/users_controller.rb:4:11:4:16 | call to params : | app/controllers/users_controller.rb:4:11:4:27 | ...[...] | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get_1 | Route handler |
| app/controllers/users_controller.rb:9:16:9:21 | call to params | app/controllers/users_controller.rb:9:16:9:21 | call to params : | app/controllers/users_controller.rb:10:42:10:49 | password | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:8:3:11:5 | login_get_2 | Route handler |
| app/controllers/users_controller.rb:14:17:14:22 | call to params | app/controllers/users_controller.rb:14:17:14:22 | call to params : | app/controllers/users_controller.rb:15:42:15:50 | @password | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:13:3:16:5 | login_get_3 | Route handler |
| app/controllers/users_controller.rb:4:11:4:16 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:3:3:6:5 | login_get_1 | Route handler |
| app/controllers/users_controller.rb:9:16:9:21 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:8:3:11:5 | login_get_2 | Route handler |
| app/controllers/users_controller.rb:14:17:14:22 | call to params | $@ for GET requests uses query parameter as sensitive data. | app/controllers/users_controller.rb:13:3:16:5 | login_get_3 | Route handler |

View File

@@ -328,7 +328,6 @@ private module Config implements FullStateConfigSig {
}
private import Impl<Config> as I
import I
/**
* A `Node` augmented with a call context (except for sinks), an access path, and a configuration.
@@ -379,6 +378,8 @@ class PathNode instanceof I::PathNode {
final predicate isSinkGroup(string group) { super.isSinkGroup(group) }
}
module PathGraph = I::PathGraph;
private predicate hasFlow(Node source, Node sink, Configuration config) {
exists(PathNode source0, PathNode sink0 |
hasFlowPath(source0, sink0, config) and
@@ -388,7 +389,7 @@ private predicate hasFlow(Node source, Node sink, Configuration config) {
}
private predicate hasFlowPath(PathNode source, PathNode sink, Configuration config) {
flowPath(source, sink) and source.getConfiguration() = config
I::flowPath(source, sink) and source.getConfiguration() = config
}
private predicate hasFlowTo(Node sink, Configuration config) { hasFlow(_, sink, config) }