mirror of
https://github.com/github/codeql.git
synced 2026-07-05 11:35:30 +02:00
Compare commits
162 Commits
codeql-cli
...
rb/data-fl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27e1a8bd7a | ||
|
|
d4b018f242 | ||
|
|
25f0382fce | ||
|
|
af5a378572 | ||
|
|
9a38e31baa | ||
|
|
ff20908bbd | ||
|
|
25dd8db423 | ||
|
|
5fa49b3319 | ||
|
|
8b85744d3e | ||
|
|
4ae90e35d5 | ||
|
|
472a10fd54 | ||
|
|
0f1b3486de | ||
|
|
a195ea942e | ||
|
|
cf4a3e0bbe | ||
|
|
fe8945b5c9 | ||
|
|
bd2a065562 | ||
|
|
2619f3f667 | ||
|
|
ab4e341e65 | ||
|
|
9da5ec79c5 | ||
|
|
e549f15b1c | ||
|
|
056b1e8d63 | ||
|
|
9be2512050 | ||
|
|
b4b34cc994 | ||
|
|
12ce46e4b1 | ||
|
|
38955d1761 | ||
|
|
9f59b6b439 | ||
|
|
0a8f39fe96 | ||
|
|
ff02ba5965 | ||
|
|
017157820a | ||
|
|
b29ac5249e | ||
|
|
4ed61c13f8 | ||
|
|
b632e21ba0 | ||
|
|
06ec03de74 | ||
|
|
046e669c78 | ||
|
|
77d1788619 | ||
|
|
2546d09fe2 | ||
|
|
515b8366d2 | ||
|
|
c8f7519cee | ||
|
|
1f644a9c1d | ||
|
|
436cc60138 | ||
|
|
156964bfc9 | ||
|
|
67772bbc43 | ||
|
|
8976ba5583 | ||
|
|
ac4cac889f | ||
|
|
65add15416 | ||
|
|
aab1e1f5b4 | ||
|
|
2148e8be4d | ||
|
|
30f5fb6d83 | ||
|
|
5ff98cd80e | ||
|
|
bcfe4ece6f | ||
|
|
cac2e2e2e4 | ||
|
|
22adf21dd3 | ||
|
|
1d2087b92a | ||
|
|
0ffb0f6d4d | ||
|
|
86e99c497d | ||
|
|
07d90b34df | ||
|
|
669b0c35fe | ||
|
|
85cd7f9121 | ||
|
|
f523fbc9d0 | ||
|
|
3973e1ce04 | ||
|
|
88c4a2f6e2 | ||
|
|
90d4861b70 | ||
|
|
80f7d58fae | ||
|
|
fd226c51c1 | ||
|
|
15e5faf5b6 | ||
|
|
d585839b7e | ||
|
|
0192ae8331 | ||
|
|
b3855b089a | ||
|
|
ccaa12998d | ||
|
|
e5663574fe | ||
|
|
00800017fd | ||
|
|
9fb436e22b | ||
|
|
269c27757d | ||
|
|
5319216c18 | ||
|
|
88c6453fa6 | ||
|
|
c0593c945b | ||
|
|
6bd09b1858 | ||
|
|
42d6968c20 | ||
|
|
408968a417 | ||
|
|
84ae17dcbb | ||
|
|
5e28e5a170 | ||
|
|
bd62f2be0e | ||
|
|
04f6debb88 | ||
|
|
dbdf6ea489 | ||
|
|
cf7a5f877b | ||
|
|
7a8c9e7644 | ||
|
|
2e6d6e1538 | ||
|
|
1fe9e8457f | ||
|
|
b29ed3b85a | ||
|
|
a56ed88db2 | ||
|
|
3fd2b9ad7b | ||
|
|
ee7970afcb | ||
|
|
db58e3357b | ||
|
|
925fd2eb45 | ||
|
|
6fe0de8a9e | ||
|
|
7faea53c18 | ||
|
|
8e8fb3d34f | ||
|
|
7a43bdbf05 | ||
|
|
352c20b0c8 | ||
|
|
be7693283b | ||
|
|
cf35299d08 | ||
|
|
d26b0892cf | ||
|
|
038bdecad7 | ||
|
|
9d1af76c02 | ||
|
|
7559d3095f | ||
|
|
db699ae314 | ||
|
|
ac013f9d19 | ||
|
|
4b5674af32 | ||
|
|
ad915e2698 | ||
|
|
138643519c | ||
|
|
661106c1a0 | ||
|
|
2e9c8c759c | ||
|
|
f2749a8878 | ||
|
|
9df725901b | ||
|
|
8c2c28dd56 | ||
|
|
9bc0c98b8e | ||
|
|
291330c7e1 | ||
|
|
7144383505 | ||
|
|
f9df8a645f | ||
|
|
8813aea893 | ||
|
|
c22a7e1c81 | ||
|
|
37b405f134 | ||
|
|
22bd10132f | ||
|
|
b65f49bd50 | ||
|
|
7b181a2de0 | ||
|
|
6830c2f355 | ||
|
|
9abaa5c0b3 | ||
|
|
492d5aec78 | ||
|
|
5b1e138300 | ||
|
|
495f744cd3 | ||
|
|
05d9c7b892 | ||
|
|
83dc6d1564 | ||
|
|
74a4061508 | ||
|
|
c2a2d6b379 | ||
|
|
e2c9240973 | ||
|
|
3e476f96bd | ||
|
|
0bc57410a0 | ||
|
|
861377f650 | ||
|
|
027b71381a | ||
|
|
65fd9cbf9c | ||
|
|
35c1d311c5 | ||
|
|
8de7df9c21 | ||
|
|
4d87abed0e | ||
|
|
5f7fa6f915 | ||
|
|
f41fd81965 | ||
|
|
9767064310 | ||
|
|
1221cbaee7 | ||
|
|
13018150ed | ||
|
|
8eccae1cdd | ||
|
|
f96e4eb87e | ||
|
|
227b10adf6 | ||
|
|
24c6bb4c52 | ||
|
|
3da3a278ab | ||
|
|
76ff593cc5 | ||
|
|
7d78df25bf | ||
|
|
ce5631e7cb | ||
|
|
12cb099376 | ||
|
|
398b2a392f | ||
|
|
5496b11153 | ||
|
|
4258147edf | ||
|
|
964c92418c | ||
|
|
bcab9d8e7c |
13
.github/workflows/atm-check-queries-run.yml
vendored
Normal file
13
.github/workflows/atm-check-queries-run.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: ATM Check Queries Run
|
||||
|
||||
# This check is required, therefore we must run it on all PRs, even if only Markdown has changed.
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
hello-world:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: foo
|
||||
run: echo "Hello world"
|
||||
13
.github/workflows/swift-qltest.yml
vendored
13
.github/workflows/swift-qltest.yml
vendored
@@ -23,12 +23,23 @@ jobs:
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
- name: Check QL formatting
|
||||
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
|
||||
qltest-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: bazelbuild/setup-bazelisk@v2
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version-file: 'swift/.python-version'
|
||||
- name: Test qltest.sh
|
||||
run: |
|
||||
bazel test //swift/tools/test/qltest
|
||||
qltest:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os : [ubuntu-20.04, macos-latest]
|
||||
os: [ ubuntu-20.04, macos-latest ]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: ./.github/actions/fetch-codeql
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-all
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups: cpp
|
||||
dbscheme: semmlecode.cpp.dbscheme
|
||||
extractor: cpp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/cpp-queries
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups:
|
||||
- cpp
|
||||
- queries
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
| test.cpp:4:26:4:26 | c<<expression>> |
|
||||
| test.cpp:4:26:4:26 | c<<unnamed>> |
|
||||
| test.cpp:5:29:5:29 | e |
|
||||
| test.cpp:6:24:6:24 | f |
|
||||
| test.cpp:6:26:6:26 | (unnamed parameter 0) |
|
||||
| test.cpp:6:29:6:31 | (unnamed parameter 1) |
|
||||
| test.cpp:7:20:7:20 | f |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-all
|
||||
version: 1.3.2
|
||||
version: 1.3.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-solorigate-queries
|
||||
version: 1.3.2
|
||||
version: 1.3.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- solorigate
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-all
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups: csharp
|
||||
dbscheme: semmlecode.csharp.dbscheme
|
||||
extractor: csharp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/csharp-queries
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups:
|
||||
- csharp
|
||||
- queries
|
||||
|
||||
@@ -273,4 +273,71 @@ The ``<module_expression>`` itself can be a module name, a selection, or a quali
|
||||
reference. For more information, see ":ref:`name-resolution`."
|
||||
|
||||
For information about how import statements are looked up, see "`Module resolution <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#module-resolution>`__"
|
||||
in the QL language specification.
|
||||
in the QL language specification.
|
||||
|
||||
Built-in modules
|
||||
****************
|
||||
|
||||
QL defines a ``QlBuiltins`` module that is always in scope.
|
||||
Currently, it defines a single parameterized sub-module
|
||||
``EquivalenceRelation``, that provides an efficient abstraction for working with
|
||||
(partial) equivalence relations in QL.
|
||||
|
||||
Equivalence relations
|
||||
=====================
|
||||
|
||||
The built-in ``EquivalenceRelation`` module is parameterized by a type ``T`` and a
|
||||
binary base relation ``base`` on ``T``. The symmetric and transitive closure of ``base``
|
||||
induces a partial equivalence relation on ``T``. If every value of ``T`` appears in
|
||||
``base``, then the induced relation is an equivalence relation on ``T``.
|
||||
|
||||
The ``EquivalenceRelation`` module exports a ``getEquivalenceClass`` predicate that
|
||||
gets the equivalence class, if any, associated with a given ``T`` element by the
|
||||
(partial) equivalence relation induced by ``base``.
|
||||
|
||||
The following example illustrates an application of the ``EquivalenceRelation``
|
||||
module to generate a custom equivalence relation:
|
||||
|
||||
.. code-block:: ql
|
||||
|
||||
class Node extends int {
|
||||
Node() { this in [1 .. 6] }
|
||||
}
|
||||
|
||||
predicate base(Node x, Node y) {
|
||||
x = 1 and y = 2
|
||||
or
|
||||
x = 3 and y = 4
|
||||
}
|
||||
|
||||
module Equiv = QlBuiltins::EquivalenceRelation<Node, base/2>;
|
||||
|
||||
from int x, int y
|
||||
where Equiv::getEquivalenceClass(x) = Equiv::getEquivalenceClass(y)
|
||||
select x, y
|
||||
|
||||
Since ``base`` does not relate ``5`` or ``6`` to any nodes, the induced
|
||||
relation is a partial equivalence relation on ``Node`` and does not relate ``5``
|
||||
or ``6`` to any nodes either.
|
||||
|
||||
The above select clause returns the following partial equivalence relation:
|
||||
|
||||
+---+---+
|
||||
| x | y |
|
||||
+===+===+
|
||||
| 1 | 1 |
|
||||
+---+---+
|
||||
| 1 | 2 |
|
||||
+---+---+
|
||||
| 2 | 1 |
|
||||
+---+---+
|
||||
| 2 | 2 |
|
||||
+---+---+
|
||||
| 3 | 3 |
|
||||
+---+---+
|
||||
| 3 | 4 |
|
||||
+---+---+
|
||||
| 4 | 3 |
|
||||
+---+---+
|
||||
| 4 | 4 |
|
||||
+---+---+
|
||||
|
||||
@@ -639,7 +639,7 @@ Various kinds of syntax can have *annotations* applied to them. Annotations are
|
||||
| "override"
|
||||
| "query"
|
||||
|
||||
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt") "]"
|
||||
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
|
||||
| "language" "[" "monotonicAggregates" "]"
|
||||
| "bindingset" "[" (variable ( "," variable)*)? "]"
|
||||
|
||||
@@ -687,17 +687,19 @@ Parameterized annotations take some additional arguments.
|
||||
|
||||
The parameterized annotation ``pragma`` supplies compiler pragmas, and may be applied in various contexts depending on the pragma in question.
|
||||
|
||||
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases |
|
||||
+==============+=========+============+===================+=======================+=========+========+=========+=========+
|
||||
| ``inline`` | | yes | yes | yes | | | | |
|
||||
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``noinline`` | | yes | yes | yes | | | | |
|
||||
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``nomagic`` | | yes | yes | yes | | | | |
|
||||
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``noopt`` | | yes | yes | yes | | | | |
|
||||
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases |
|
||||
+===========================+=========+============+===================+=======================+=========+========+=========+=========+
|
||||
| ``inline`` | | yes | yes | yes | | | | |
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``noinline`` | | yes | yes | yes | | | | |
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``nomagic`` | | yes | yes | yes | | | | |
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``noopt`` | | yes | yes | yes | | | | |
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
| ``assume_small_delta`` | | yes | yes | yes | | | | |
|
||||
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
|
||||
|
||||
The parameterized annotation ``language`` supplies language pragmas which change the behavior of the language. Language pragmas apply at the scope level, and are inherited by nested scopes.
|
||||
|
||||
@@ -2048,7 +2050,7 @@ The complete grammar for QL is as follows:
|
||||
| "override"
|
||||
| "query"
|
||||
|
||||
argsAnnotation ::= "pragma" "[" ("noinline" | "nomagic" | "noopt") "]"
|
||||
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
|
||||
| "language" "[" "monotonicAggregates" "]"
|
||||
| "bindingset" "[" (variable ( "," variable)*)? "]"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-all
|
||||
version: 0.3.2
|
||||
version: 0.3.3-dev
|
||||
groups: go
|
||||
dbscheme: go.dbscheme
|
||||
extractor: go
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/go-queries
|
||||
version: 0.3.2
|
||||
version: 0.3.3-dev
|
||||
groups:
|
||||
- go
|
||||
- queries
|
||||
|
||||
@@ -35,8 +35,6 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
|
||||
if (ret) externalDeclWorkList.add(Pair(d, signature))
|
||||
return ret
|
||||
}
|
||||
fun extractLater(p: IrProperty) = extractLater(p, propertySignature)
|
||||
fun extractLater(f: IrField) = extractLater(f, fieldSignature)
|
||||
fun extractLater(c: IrClass) = extractLater(c, "")
|
||||
|
||||
fun extractExternalClasses() {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.github.codeql
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
@@ -139,6 +140,8 @@ class KotlinExtractorExtension(
|
||||
logger.flush()
|
||||
logger.info("Extraction for invocation TRAP file $invocationTrapFile")
|
||||
logger.flush()
|
||||
logger.info("Kotlin version ${KotlinCompilerVersion.getVersion()}")
|
||||
logger.flush()
|
||||
logPeakMemoryUsage(logger, "before extractor")
|
||||
if (System.getenv("CODEQL_EXTRACTOR_JAVA_KOTLIN_DUMP") == "true") {
|
||||
logger.info("moduleFragment:\n" + moduleFragment.dump())
|
||||
|
||||
@@ -305,7 +305,7 @@ open class KotlinFileExtractor(
|
||||
val kind = c.kind
|
||||
if (kind == ClassKind.ENUM_CLASS) {
|
||||
tw.writeIsEnumType(classId)
|
||||
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
|
||||
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
|
||||
logger.errorElement("Unrecognised class kind $kind", c)
|
||||
}
|
||||
}
|
||||
@@ -452,7 +452,7 @@ open class KotlinFileExtractor(
|
||||
val kind = c.kind
|
||||
if (kind == ClassKind.ENUM_CLASS) {
|
||||
tw.writeIsEnumType(classId)
|
||||
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) {
|
||||
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
|
||||
logger.warnElement("Unrecognised class kind $kind", c)
|
||||
}
|
||||
|
||||
@@ -1884,7 +1884,7 @@ open class KotlinFileExtractor(
|
||||
IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
|
||||
)
|
||||
|
||||
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx)
|
||||
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true)
|
||||
}
|
||||
|
||||
private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? {
|
||||
@@ -1961,8 +1961,12 @@ open class KotlinFileExtractor(
|
||||
superQualifierSymbol: IrClassSymbol? = null) {
|
||||
|
||||
val locId = tw.getLocation(locElement)
|
||||
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
|
||||
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
|
||||
// as omitting it already implies passing an empty vararg array.
|
||||
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
|
||||
|
||||
if (valueArguments.any { it == null }) {
|
||||
if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
|
||||
extractsDefaultsCall(
|
||||
syntacticCallTarget,
|
||||
locId,
|
||||
@@ -2082,11 +2086,11 @@ open class KotlinFileExtractor(
|
||||
private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) =
|
||||
extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset)
|
||||
|
||||
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) {
|
||||
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int, extractVarargAsArray: Boolean = false) {
|
||||
var i = 0
|
||||
valueArguments.forEach { arg ->
|
||||
if(arg != null) {
|
||||
if (arg is IrVararg) {
|
||||
if (arg is IrVararg && !extractVarargAsArray) {
|
||||
arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) }
|
||||
i += arg.elements.size
|
||||
} else {
|
||||
|
||||
@@ -253,19 +253,24 @@ open class KotlinUsesExtractor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun propertySignature(p: IrProperty) =
|
||||
((p.getter ?: p.setter)?.extensionReceiverParameter?.let { useType(erase(it.type)).javaResult.signature } ?: "")
|
||||
|
||||
private fun extractPropertyLaterIfExternalFileMember(p: IrProperty) {
|
||||
if (isExternalFileClassMember(p)) {
|
||||
extractExternalClassLater(p.parentAsClass)
|
||||
dependencyCollector?.addDependency(p, externalClassExtractor.propertySignature)
|
||||
externalClassExtractor.extractLater(p)
|
||||
val signature = propertySignature(p) + externalClassExtractor.propertySignature
|
||||
dependencyCollector?.addDependency(p, signature)
|
||||
externalClassExtractor.extractLater(p, signature)
|
||||
}
|
||||
}
|
||||
|
||||
private fun extractFieldLaterIfExternalFileMember(f: IrField) {
|
||||
if (isExternalFileClassMember(f)) {
|
||||
extractExternalClassLater(f.parentAsClass)
|
||||
dependencyCollector?.addDependency(f, externalClassExtractor.fieldSignature)
|
||||
externalClassExtractor.extractLater(f)
|
||||
val signature = (f.correspondingPropertySymbol?.let { propertySignature(it.owner) } ?: "") + externalClassExtractor.fieldSignature
|
||||
dependencyCollector?.addDependency(f, signature)
|
||||
externalClassExtractor.extractLater(f, signature)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,7 +818,7 @@ open class KotlinUsesExtractor(
|
||||
OperatorNameConventions.INVOKE.asString())
|
||||
|
||||
fun getSuffixIfInternal() =
|
||||
if (f.visibility == DescriptorVisibilities.INTERNAL) {
|
||||
if (f.visibility == DescriptorVisibilities.INTERNAL && f !is IrConstructor) {
|
||||
"\$" + getJvmModuleName(f)
|
||||
} else {
|
||||
""
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package com.github.codeql
|
||||
|
||||
import com.github.codeql.utils.versions.Psi2Ir
|
||||
import com.github.codeql.utils.versions.getPsi2Ir
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiWhiteSpace
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
|
||||
@@ -15,9 +16,16 @@ class LinesOfCode(
|
||||
val tw: FileTrapWriter,
|
||||
val file: IrFile
|
||||
) {
|
||||
val psi2Ir = Psi2Ir(logger)
|
||||
val psi2Ir = getPsi2Ir(logger).also {
|
||||
if (it == null) {
|
||||
logger.warn("Lines of code will not be populated as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
|
||||
}
|
||||
}
|
||||
|
||||
fun linesOfCodeInFile(id: Label<DbFile>) {
|
||||
if (psi2Ir == null) {
|
||||
return
|
||||
}
|
||||
val ktFile = psi2Ir.getKtFile(file)
|
||||
if (ktFile == null) {
|
||||
return
|
||||
@@ -26,6 +34,9 @@ class LinesOfCode(
|
||||
}
|
||||
|
||||
fun linesOfCodeInDeclaration(d: IrDeclaration, id: Label<out DbSourceline>) {
|
||||
if (psi2Ir == null) {
|
||||
return
|
||||
}
|
||||
val p = psi2Ir.findPsiElement(d, file)
|
||||
if (p == null) {
|
||||
return
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.github.codeql.comments
|
||||
import com.github.codeql.*
|
||||
import com.github.codeql.utils.IrVisitorLookup
|
||||
import com.github.codeql.utils.isLocalFunction
|
||||
import com.github.codeql.utils.versions.Psi2Ir
|
||||
import com.github.codeql.utils.versions.getPsi2Ir
|
||||
import com.github.codeql.utils.versions.Psi2IrFacade
|
||||
import com.intellij.psi.PsiComment
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
@@ -21,18 +23,23 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
|
||||
class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label<out DbFile>) {
|
||||
private val tw = fileExtractor.tw
|
||||
private val logger = fileExtractor.logger
|
||||
private val psi2Ir = Psi2Ir(logger)
|
||||
private val ktFile = psi2Ir.getKtFile(file)
|
||||
|
||||
fun extract() {
|
||||
val psi2Ir = getPsi2Ir(logger)
|
||||
if (psi2Ir == null) {
|
||||
logger.warn("Comments will not be extracted as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
|
||||
return
|
||||
}
|
||||
val ktFile = psi2Ir.getKtFile(file)
|
||||
if (ktFile == null) {
|
||||
logger.warn("Comments are not being processed in ${file.path}.")
|
||||
} else {
|
||||
ktFile.accept(commentVisitor)
|
||||
return
|
||||
}
|
||||
val commentVisitor = mkCommentVisitor(psi2Ir)
|
||||
ktFile.accept(commentVisitor)
|
||||
}
|
||||
|
||||
private val commentVisitor =
|
||||
private fun mkCommentVisitor(psi2Ir: Psi2IrFacade): KtVisitor<Unit, Unit> =
|
||||
object : KtVisitor<Unit, Unit>() {
|
||||
override fun visitElement(element: PsiElement) {
|
||||
element.acceptChildren(this)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package com.github.codeql.utils
|
||||
|
||||
import com.github.codeql.utils.versions.Psi2Ir
|
||||
import com.github.codeql.utils.versions.Psi2IrFacade
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
|
||||
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.ir.util.isFakeOverride
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
|
||||
|
||||
class IrVisitorLookup(private val psi2Ir: Psi2Ir, private val psi: PsiElement, private val file: IrFile) :
|
||||
class IrVisitorLookup(private val psi2Ir: Psi2IrFacade, private val psi: PsiElement, private val file: IrFile) :
|
||||
IrElementVisitor<Unit, MutableCollection<IrElement>> {
|
||||
private val location = psi.getLocation()
|
||||
|
||||
|
||||
@@ -1,19 +1,5 @@
|
||||
package com.github.codeql.utils.versions
|
||||
|
||||
import com.github.codeql.FileLogger
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade {
|
||||
override fun getKtFile(irFile: IrFile): KtFile? {
|
||||
logger.warn("Comment extraction is not supported for Kotlin < 1.5.20")
|
||||
return null
|
||||
}
|
||||
|
||||
override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
|
||||
logger.error("Attempted comment extraction for Kotlin < 1.5.20")
|
||||
return null
|
||||
}
|
||||
}
|
||||
fun getPsi2Ir(@Suppress("UNUSED_PARAMETER") logger: FileLogger): Psi2IrFacade? = null
|
||||
|
||||
@@ -8,7 +8,9 @@ import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
|
||||
class Psi2Ir(private val logger: FileLogger): Psi2IrFacade {
|
||||
fun getPsi2Ir(logger: FileLogger): Psi2IrFacade? = Psi2Ir(logger)
|
||||
|
||||
private class Psi2Ir(private val logger: FileLogger): Psi2IrFacade {
|
||||
override fun getKtFile(irFile: IrFile): KtFile? {
|
||||
return irFile.getKtFile()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
| user.kt:3:22:3:22 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |
|
||||
| user.kt:3:28:3:28 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |
|
||||
@@ -0,0 +1,7 @@
|
||||
package lib
|
||||
|
||||
val String.f
|
||||
get() = 1
|
||||
|
||||
val Int.f
|
||||
get() = 2
|
||||
@@ -0,0 +1,4 @@
|
||||
from create_database_utils import *
|
||||
|
||||
runSuccessfully(["kotlinc", "test.kt", "-d", "lib"])
|
||||
run_codeql_database_create(["kotlinc user.kt -cp lib"], lang="java")
|
||||
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from MethodAccess ma
|
||||
select ma, ma.getCallee()
|
||||
@@ -0,0 +1,3 @@
|
||||
import lib.f
|
||||
|
||||
fun test() = "hello".f + 1.f
|
||||
@@ -32,6 +32,7 @@ with open('logs.csv', 'w', newline='') as f_out:
|
||||
j = json.loads(line)
|
||||
msg = j['message']
|
||||
msg = re.sub('(?<=Extraction for invocation TRAP file ).*/kt-db/trap/java/invocations/kotlin\..*\.trap', '<FILENAME>', msg)
|
||||
msg = re.sub('(?<=Kotlin version )[0-9.]+', '<VERSION>', msg)
|
||||
if msg.startswith('Peak memory: '):
|
||||
# Peak memory information varies from run to run, so just ignore it
|
||||
continue
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
| 1 | 1 | Test script | Log file | 1 |
|
||||
| 1 | 2 | CodeQL Kotlin extractor | INFO | Extraction started |
|
||||
| 1 | 3 | CodeQL Kotlin extractor | INFO | Extraction for invocation TRAP file <FILENAME> |
|
||||
| 1 | 4 | CodeQL Kotlin extractor | INFO | Extracting file test.kt |
|
||||
| 1 | 5 | CodeQL Kotlin extractor | INFO | Extraction completed |
|
||||
| 1 | 4 | CodeQL Kotlin extractor | INFO | Kotlin version <VERSION> |
|
||||
| 1 | 5 | CodeQL Kotlin extractor | INFO | Extracting file test.kt |
|
||||
| 1 | 6 | CodeQL Kotlin extractor | INFO | Extraction completed |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-all
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups: java
|
||||
dbscheme: config/semmlecode.dbscheme
|
||||
extractor: java
|
||||
|
||||
@@ -2367,14 +2367,33 @@ class Argument extends Expr {
|
||||
*/
|
||||
predicate isNthVararg(int arrayindex) {
|
||||
not this.isExplicitVarargsArray() and
|
||||
exists(Callable tgt, int varargsParamPos |
|
||||
exists(Callable tgt |
|
||||
call.getCallee() = tgt and
|
||||
tgt.getParameter(varargsParamPos).isVarargs() and
|
||||
arrayindex = pos - varargsParamPos and
|
||||
arrayindex = pos - tgt.getVaragsParameterIndex() and
|
||||
arrayindex >= 0 and
|
||||
arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter position that will receive this argument.
|
||||
*
|
||||
* For all vararg arguments, this is the position of the vararg array parameter.
|
||||
*/
|
||||
int getParameterPos() {
|
||||
exists(Callable c | c = call.getCallee() |
|
||||
if c.isVarargs()
|
||||
then
|
||||
if pos < c.getVaragsParameterIndex()
|
||||
then result = pos // Vararg method argument, before the vararg parameter
|
||||
else (
|
||||
if this.isVararg()
|
||||
then result = c.getVaragsParameterIndex() // Part of the implicit vararg array
|
||||
else result = pos - (call.getNumArgument() - c.getNumberOfParameters()) // Vararg method argument, after the vararg parameter (offset could be -1 in the zero-vararg case)
|
||||
)
|
||||
else result = pos // Not a vararg method
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -283,6 +283,9 @@ class Callable extends StmtParent, Member, @callable {
|
||||
/** Holds if the last parameter of this callable is a varargs (variable arity) parameter. */
|
||||
predicate isVarargs() { this.getAParameter().isVarargs() }
|
||||
|
||||
/** Gets the index of this callable's varargs parameter, if any exists. */
|
||||
int getVaragsParameterIndex() { this.getParameter(result).isVarargs() }
|
||||
|
||||
/**
|
||||
* Gets the signature of this callable, where all types in the signature have a fully-qualified name.
|
||||
* The parameter types are only separated by a comma (without space). If this callable has
|
||||
|
||||
@@ -378,11 +378,11 @@ module Private {
|
||||
*/
|
||||
predicate argumentOf(DataFlowCall call, int pos) {
|
||||
exists(Argument arg | this.asExpr() = arg |
|
||||
call.asCall() = arg.getCall() and pos = arg.getPosition()
|
||||
call.asCall() = arg.getCall() and pos = arg.getParameterPos()
|
||||
)
|
||||
or
|
||||
call.asCall() = this.(ImplicitVarargsArray).getCall() and
|
||||
pos = call.asCall().getCallee().getNumberOfParameters() - 1
|
||||
pos = call.asCall().getCallee().getVaragsParameterIndex()
|
||||
or
|
||||
pos = -1 and this = getInstanceArgument(call.asCall())
|
||||
or
|
||||
|
||||
@@ -282,13 +282,8 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
|
||||
* Converts an argument index to a formal parameter index.
|
||||
* This is relevant for varadic methods.
|
||||
*/
|
||||
private int argToParam(Call call, int arg) {
|
||||
exists(call.getArgument(arg)) and
|
||||
exists(Callable c | c = call.getCallee() |
|
||||
if c.isVarargs() and arg >= c.getNumberOfParameters()
|
||||
then result = c.getNumberOfParameters() - 1
|
||||
else result = arg
|
||||
)
|
||||
private int argToParam(Call call, int argIdx) {
|
||||
result = call.getArgument(argIdx).(Argument).getParameterPos()
|
||||
}
|
||||
|
||||
/** Access to a method that passes taint from qualifier to argument. */
|
||||
|
||||
@@ -22,7 +22,7 @@ class IdentifierElement extends Element {
|
||||
|
||||
from IdentifierElement e, string msg
|
||||
where
|
||||
e.fromSource() and
|
||||
e.getCompilationUnit().isJavaSourceFile() and
|
||||
not e.(Constructor).isDefaultConstructor() and
|
||||
(
|
||||
e.getName() = "_" and
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/java-queries
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups:
|
||||
- java
|
||||
- queries
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
public class User {
|
||||
|
||||
public static void test() { new Test(1, 2); }
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
| User.java:3:31:3:44 | new Test(...) | test.kt:3:3:3:51 | { ... } |
|
||||
@@ -0,0 +1,5 @@
|
||||
public class Test() {
|
||||
|
||||
internal constructor(x: Int, y: Int) : this() { }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from ClassInstanceExpr ce
|
||||
select ce, ce.getConstructor().getBody()
|
||||
4
java/ql/test/kotlin/library-tests/methods/diagnostics.ql
Normal file
4
java/ql/test/kotlin/library-tests/methods/diagnostics.ql
Normal file
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
import semmle.code.java.Diagnostics
|
||||
|
||||
select any(Diagnostic d | not d.toString().matches("Not rewriting trap file for%"))
|
||||
@@ -7,6 +7,394 @@ test.kt:
|
||||
# 1| 0: [Parameter] a
|
||||
# 1| 0: [TypeAccess] Object
|
||||
# 1| 5: [BlockStmt] { ... }
|
||||
# 184| 2: [Method] varargsTest
|
||||
# 184| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 184| 0: [Parameter] x
|
||||
# 184| 0: [TypeAccess] String
|
||||
# 184| 1: [Parameter] y
|
||||
# 184| 0: [TypeAccess] String[]
|
||||
# 184| 0: [WildcardTypeAccess] ? ...
|
||||
# 184| 0: [TypeAccess] String
|
||||
# 184| 2: [Parameter] z
|
||||
# 184| 0: [TypeAccess] String
|
||||
# 184| 5: [BlockStmt] { ... }
|
||||
# 185| 0: [ExprStmt] <Expr>;
|
||||
# 185| 0: [MethodAccess] sink(...)
|
||||
# 185| -1: [TypeAccess] TestKt
|
||||
# 185| 0: [VarAccess] x
|
||||
# 186| 1: [ExprStmt] <Expr>;
|
||||
# 186| 0: [MethodAccess] sink(...)
|
||||
# 186| -1: [TypeAccess] TestKt
|
||||
# 186| 0: [ArrayAccess] ...[...]
|
||||
# 186| 0: [VarAccess] y
|
||||
# 186| 1: [IntegerLiteral] 0
|
||||
# 187| 2: [ExprStmt] <Expr>;
|
||||
# 187| 0: [MethodAccess] sink(...)
|
||||
# 187| -1: [TypeAccess] TestKt
|
||||
# 187| 0: [VarAccess] z
|
||||
# 184| 3: [Method] varargsTest$default
|
||||
# 184| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 184| 0: [Parameter] p0
|
||||
# 184| 0: [TypeAccess] String
|
||||
# 184| 1: [Parameter] p1
|
||||
# 184| 0: [TypeAccess] String[]
|
||||
# 184| 2: [Parameter] p2
|
||||
# 184| 0: [TypeAccess] String
|
||||
# 184| 3: [Parameter] p3
|
||||
# 184| 0: [TypeAccess] int
|
||||
# 184| 4: [Parameter] p4
|
||||
# 184| 0: [TypeAccess] Object
|
||||
# 184| 5: [BlockStmt] { ... }
|
||||
# 184| 0: [IfStmt] if (...)
|
||||
# 184| 0: [EQExpr] ... == ...
|
||||
# 184| 0: [AndBitwiseExpr] ... & ...
|
||||
# 184| 0: [IntegerLiteral] 1
|
||||
# 184| 1: [VarAccess] p3
|
||||
# 184| 1: [IntegerLiteral] 0
|
||||
# 184| 1: [ExprStmt] <Expr>;
|
||||
# 184| 0: [AssignExpr] ...=...
|
||||
# 184| 0: [VarAccess] p0
|
||||
# 184| 1: [StringLiteral] before-vararg-default sunk
|
||||
# 184| 1: [IfStmt] if (...)
|
||||
# 184| 0: [EQExpr] ... == ...
|
||||
# 184| 0: [AndBitwiseExpr] ... & ...
|
||||
# 184| 0: [IntegerLiteral] 2
|
||||
# 184| 1: [VarAccess] p3
|
||||
# 184| 1: [IntegerLiteral] 0
|
||||
# 184| 1: [ExprStmt] <Expr>;
|
||||
# 184| 0: [AssignExpr] ...=...
|
||||
# 184| 0: [VarAccess] p1
|
||||
# 184| 1: [ArrayCreationExpr] new String[]
|
||||
# 184| -2: [ArrayInit] {...}
|
||||
# 184| 0: [StringLiteral] first-vararg-default sunk
|
||||
# 184| 1: [StringLiteral] second-vararg-default sunk
|
||||
# 184| -1: [TypeAccess] String
|
||||
# 184| 0: [IntegerLiteral] 2
|
||||
# 184| 2: [IfStmt] if (...)
|
||||
# 184| 0: [EQExpr] ... == ...
|
||||
# 184| 0: [AndBitwiseExpr] ... & ...
|
||||
# 184| 0: [IntegerLiteral] 4
|
||||
# 184| 1: [VarAccess] p3
|
||||
# 184| 1: [IntegerLiteral] 0
|
||||
# 184| 1: [ExprStmt] <Expr>;
|
||||
# 184| 0: [AssignExpr] ...=...
|
||||
# 184| 0: [VarAccess] p2
|
||||
# 184| 1: [StringLiteral] after-vararg-default sunk
|
||||
# 184| 3: [ReturnStmt] return ...
|
||||
# 184| 0: [MethodAccess] varargsTest(...)
|
||||
# 184| -1: [TypeAccess] TestKt
|
||||
# 184| 0: [VarAccess] p0
|
||||
# 184| 1: [VarAccess] p1
|
||||
# 184| 2: [VarAccess] p2
|
||||
# 190| 4: [Method] varargsUser
|
||||
# 190| 3: [TypeAccess] Unit
|
||||
# 190| 5: [BlockStmt] { ... }
|
||||
# 191| 0: [ExprStmt] <Expr>;
|
||||
# 191| 0: [MethodAccess] varargsTest$default(...)
|
||||
# 191| -1: [TypeAccess] TestKt
|
||||
# 1| 0: [NullLiteral] null
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 0
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 192| 1: [ExprStmt] <Expr>;
|
||||
# 192| 0: [MethodAccess] varargsTest$default(...)
|
||||
# 192| -1: [TypeAccess] TestKt
|
||||
# 192| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 1
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 193| 2: [ExprStmt] <Expr>;
|
||||
# 193| 0: [MethodAccess] varargsTest$default(...)
|
||||
# 193| -1: [TypeAccess] TestKt
|
||||
# 193| 0: [StringLiteral] no-varargs-before sunk
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 193| 2: [StringLiteral] no-varargs-after sunk
|
||||
# 1| 3: [IntegerLiteral] 5
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 194| 3: [ExprStmt] <Expr>;
|
||||
# 194| 0: [MethodAccess] varargsTest(...)
|
||||
# 194| -1: [TypeAccess] TestKt
|
||||
# 194| 0: [StringLiteral] one-vararg-before sunk
|
||||
# 194| 1: [StringLiteral] one-vararg sunk
|
||||
# 194| 2: [StringLiteral] one-vararg-after sunk
|
||||
# 195| 4: [ExprStmt] <Expr>;
|
||||
# 195| 0: [MethodAccess] varargsTest(...)
|
||||
# 195| -1: [TypeAccess] TestKt
|
||||
# 195| 0: [StringLiteral] two-varargs-before sunk
|
||||
# 195| 1: [StringLiteral] two-vararg-first sunk
|
||||
# 195| 2: [StringLiteral] two-vararg-second sunk
|
||||
# 195| 3: [StringLiteral] two-varargs-after sunk
|
||||
# 196| 5: [ExprStmt] <Expr>;
|
||||
# 196| 0: [MethodAccess] varargsTest$default(...)
|
||||
# 196| -1: [TypeAccess] TestKt
|
||||
# 196| 0: [StringLiteral] no-z-parmeter sunk
|
||||
# 196| 1: [ArrayCreationExpr] new String[]
|
||||
# 196| -2: [ArrayInit] {...}
|
||||
# 196| 0: [StringLiteral] no-z-parameter first vararg sunk
|
||||
# 196| 1: [StringLiteral] no-z-parameter second vararg sunk
|
||||
# 196| -1: [TypeAccess] String
|
||||
# 196| 0: [IntegerLiteral] 2
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 3
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 199| 5: [Method] varargsTestOnlySinkVarargs
|
||||
# 199| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 199| 0: [Parameter] x
|
||||
# 199| 0: [TypeAccess] String
|
||||
# 199| 1: [Parameter] y
|
||||
# 199| 0: [TypeAccess] String[]
|
||||
# 199| 0: [WildcardTypeAccess] ? ...
|
||||
# 199| 0: [TypeAccess] String
|
||||
# 199| 2: [Parameter] z
|
||||
# 199| 0: [TypeAccess] String
|
||||
# 199| 5: [BlockStmt] { ... }
|
||||
# 200| 0: [ExprStmt] <Expr>;
|
||||
# 200| 0: [MethodAccess] sink(...)
|
||||
# 200| -1: [TypeAccess] TestKt
|
||||
# 200| 0: [ArrayAccess] ...[...]
|
||||
# 200| 0: [VarAccess] y
|
||||
# 200| 1: [IntegerLiteral] 0
|
||||
# 199| 6: [Method] varargsTestOnlySinkVarargs$default
|
||||
# 199| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 199| 0: [Parameter] p0
|
||||
# 199| 0: [TypeAccess] String
|
||||
# 199| 1: [Parameter] p1
|
||||
# 199| 0: [TypeAccess] String[]
|
||||
# 199| 2: [Parameter] p2
|
||||
# 199| 0: [TypeAccess] String
|
||||
# 199| 3: [Parameter] p3
|
||||
# 199| 0: [TypeAccess] int
|
||||
# 199| 4: [Parameter] p4
|
||||
# 199| 0: [TypeAccess] Object
|
||||
# 199| 5: [BlockStmt] { ... }
|
||||
# 199| 0: [IfStmt] if (...)
|
||||
# 199| 0: [EQExpr] ... == ...
|
||||
# 199| 0: [AndBitwiseExpr] ... & ...
|
||||
# 199| 0: [IntegerLiteral] 1
|
||||
# 199| 1: [VarAccess] p3
|
||||
# 199| 1: [IntegerLiteral] 0
|
||||
# 199| 1: [ExprStmt] <Expr>;
|
||||
# 199| 0: [AssignExpr] ...=...
|
||||
# 199| 0: [VarAccess] p0
|
||||
# 199| 1: [StringLiteral] before-vararg-default not sunk 2
|
||||
# 199| 1: [IfStmt] if (...)
|
||||
# 199| 0: [EQExpr] ... == ...
|
||||
# 199| 0: [AndBitwiseExpr] ... & ...
|
||||
# 199| 0: [IntegerLiteral] 2
|
||||
# 199| 1: [VarAccess] p3
|
||||
# 199| 1: [IntegerLiteral] 0
|
||||
# 199| 1: [ExprStmt] <Expr>;
|
||||
# 199| 0: [AssignExpr] ...=...
|
||||
# 199| 0: [VarAccess] p1
|
||||
# 199| 1: [ArrayCreationExpr] new String[]
|
||||
# 199| -2: [ArrayInit] {...}
|
||||
# 199| 0: [StringLiteral] first-vararg-default sunk 2
|
||||
# 199| 1: [StringLiteral] second-vararg-default sunk 2
|
||||
# 199| -1: [TypeAccess] String
|
||||
# 199| 0: [IntegerLiteral] 2
|
||||
# 199| 2: [IfStmt] if (...)
|
||||
# 199| 0: [EQExpr] ... == ...
|
||||
# 199| 0: [AndBitwiseExpr] ... & ...
|
||||
# 199| 0: [IntegerLiteral] 4
|
||||
# 199| 1: [VarAccess] p3
|
||||
# 199| 1: [IntegerLiteral] 0
|
||||
# 199| 1: [ExprStmt] <Expr>;
|
||||
# 199| 0: [AssignExpr] ...=...
|
||||
# 199| 0: [VarAccess] p2
|
||||
# 199| 1: [StringLiteral] after-vararg-default not sunk 2
|
||||
# 199| 3: [ReturnStmt] return ...
|
||||
# 199| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
|
||||
# 199| -1: [TypeAccess] TestKt
|
||||
# 199| 0: [VarAccess] p0
|
||||
# 199| 1: [VarAccess] p1
|
||||
# 199| 2: [VarAccess] p2
|
||||
# 203| 7: [Method] varargsUserOnlySinkVarargs
|
||||
# 203| 3: [TypeAccess] Unit
|
||||
# 203| 5: [BlockStmt] { ... }
|
||||
# 204| 0: [ExprStmt] <Expr>;
|
||||
# 204| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
|
||||
# 204| -1: [TypeAccess] TestKt
|
||||
# 1| 0: [NullLiteral] null
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 0
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 205| 1: [ExprStmt] <Expr>;
|
||||
# 205| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
|
||||
# 205| -1: [TypeAccess] TestKt
|
||||
# 205| 0: [StringLiteral] no-varargs-before, no-z-parameter not sunk 2
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 1
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 206| 2: [ExprStmt] <Expr>;
|
||||
# 206| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
|
||||
# 206| -1: [TypeAccess] TestKt
|
||||
# 206| 0: [StringLiteral] no-varargs-before not sunk 2
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 206| 2: [StringLiteral] no-varargs-after not sunk 2
|
||||
# 1| 3: [IntegerLiteral] 5
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 207| 3: [ExprStmt] <Expr>;
|
||||
# 207| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
|
||||
# 207| -1: [TypeAccess] TestKt
|
||||
# 207| 0: [StringLiteral] one-vararg-before not sunk 2
|
||||
# 207| 1: [StringLiteral] one-vararg sunk 2
|
||||
# 207| 2: [StringLiteral] one-vararg-after not sunk 2
|
||||
# 208| 4: [ExprStmt] <Expr>;
|
||||
# 208| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
|
||||
# 208| -1: [TypeAccess] TestKt
|
||||
# 208| 0: [StringLiteral] two-varargs-before not sunk 2
|
||||
# 208| 1: [StringLiteral] two-vararg-first sunk 2
|
||||
# 208| 2: [StringLiteral] two-vararg-second sunk 2
|
||||
# 208| 3: [StringLiteral] two-varargs-after not sunk 2
|
||||
# 209| 5: [ExprStmt] <Expr>;
|
||||
# 209| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
|
||||
# 209| -1: [TypeAccess] TestKt
|
||||
# 209| 0: [StringLiteral] no-z-parmeter not sunk 2
|
||||
# 209| 1: [ArrayCreationExpr] new String[]
|
||||
# 209| -2: [ArrayInit] {...}
|
||||
# 209| 0: [StringLiteral] no-z-parameter first vararg sunk 2
|
||||
# 209| 1: [StringLiteral] no-z-parameter second vararg sunk 2
|
||||
# 209| -1: [TypeAccess] String
|
||||
# 209| 0: [IntegerLiteral] 2
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 3
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 212| 8: [Method] varargsTestOnlySinkRegularArgs
|
||||
# 212| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 212| 0: [Parameter] x
|
||||
# 212| 0: [TypeAccess] String
|
||||
# 212| 1: [Parameter] y
|
||||
# 212| 0: [TypeAccess] String[]
|
||||
# 212| 0: [WildcardTypeAccess] ? ...
|
||||
# 212| 0: [TypeAccess] String
|
||||
# 212| 2: [Parameter] z
|
||||
# 212| 0: [TypeAccess] String
|
||||
# 212| 5: [BlockStmt] { ... }
|
||||
# 213| 0: [ExprStmt] <Expr>;
|
||||
# 213| 0: [MethodAccess] sink(...)
|
||||
# 213| -1: [TypeAccess] TestKt
|
||||
# 213| 0: [VarAccess] x
|
||||
# 214| 1: [ExprStmt] <Expr>;
|
||||
# 214| 0: [MethodAccess] sink(...)
|
||||
# 214| -1: [TypeAccess] TestKt
|
||||
# 214| 0: [VarAccess] z
|
||||
# 212| 9: [Method] varargsTestOnlySinkRegularArgs$default
|
||||
# 212| 3: [TypeAccess] Unit
|
||||
#-----| 4: (Parameters)
|
||||
# 212| 0: [Parameter] p0
|
||||
# 212| 0: [TypeAccess] String
|
||||
# 212| 1: [Parameter] p1
|
||||
# 212| 0: [TypeAccess] String[]
|
||||
# 212| 2: [Parameter] p2
|
||||
# 212| 0: [TypeAccess] String
|
||||
# 212| 3: [Parameter] p3
|
||||
# 212| 0: [TypeAccess] int
|
||||
# 212| 4: [Parameter] p4
|
||||
# 212| 0: [TypeAccess] Object
|
||||
# 212| 5: [BlockStmt] { ... }
|
||||
# 212| 0: [IfStmt] if (...)
|
||||
# 212| 0: [EQExpr] ... == ...
|
||||
# 212| 0: [AndBitwiseExpr] ... & ...
|
||||
# 212| 0: [IntegerLiteral] 1
|
||||
# 212| 1: [VarAccess] p3
|
||||
# 212| 1: [IntegerLiteral] 0
|
||||
# 212| 1: [ExprStmt] <Expr>;
|
||||
# 212| 0: [AssignExpr] ...=...
|
||||
# 212| 0: [VarAccess] p0
|
||||
# 212| 1: [StringLiteral] before-vararg-default sunk 3
|
||||
# 212| 1: [IfStmt] if (...)
|
||||
# 212| 0: [EQExpr] ... == ...
|
||||
# 212| 0: [AndBitwiseExpr] ... & ...
|
||||
# 212| 0: [IntegerLiteral] 2
|
||||
# 212| 1: [VarAccess] p3
|
||||
# 212| 1: [IntegerLiteral] 0
|
||||
# 212| 1: [ExprStmt] <Expr>;
|
||||
# 212| 0: [AssignExpr] ...=...
|
||||
# 212| 0: [VarAccess] p1
|
||||
# 212| 1: [ArrayCreationExpr] new String[]
|
||||
# 212| -2: [ArrayInit] {...}
|
||||
# 212| 0: [StringLiteral] first-vararg-default not sunk 3
|
||||
# 212| 1: [StringLiteral] second-vararg-default not sunk 3
|
||||
# 212| -1: [TypeAccess] String
|
||||
# 212| 0: [IntegerLiteral] 2
|
||||
# 212| 2: [IfStmt] if (...)
|
||||
# 212| 0: [EQExpr] ... == ...
|
||||
# 212| 0: [AndBitwiseExpr] ... & ...
|
||||
# 212| 0: [IntegerLiteral] 4
|
||||
# 212| 1: [VarAccess] p3
|
||||
# 212| 1: [IntegerLiteral] 0
|
||||
# 212| 1: [ExprStmt] <Expr>;
|
||||
# 212| 0: [AssignExpr] ...=...
|
||||
# 212| 0: [VarAccess] p2
|
||||
# 212| 1: [StringLiteral] after-vararg-default sunk 3
|
||||
# 212| 3: [ReturnStmt] return ...
|
||||
# 212| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
|
||||
# 212| -1: [TypeAccess] TestKt
|
||||
# 212| 0: [VarAccess] p0
|
||||
# 212| 1: [VarAccess] p1
|
||||
# 212| 2: [VarAccess] p2
|
||||
# 217| 10: [Method] varargsUserOnlySinkRegularArgs
|
||||
# 217| 3: [TypeAccess] Unit
|
||||
# 217| 5: [BlockStmt] { ... }
|
||||
# 218| 0: [ExprStmt] <Expr>;
|
||||
# 218| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
|
||||
# 218| -1: [TypeAccess] TestKt
|
||||
# 1| 0: [NullLiteral] null
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 0
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 219| 1: [ExprStmt] <Expr>;
|
||||
# 219| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
|
||||
# 219| -1: [TypeAccess] TestKt
|
||||
# 219| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk 3
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 1
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 220| 2: [ExprStmt] <Expr>;
|
||||
# 220| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
|
||||
# 220| -1: [TypeAccess] TestKt
|
||||
# 220| 0: [StringLiteral] no-varargs-before sunk 3
|
||||
# 1| 1: [NullLiteral] null
|
||||
# 220| 2: [StringLiteral] no-varargs-after sunk 3
|
||||
# 1| 3: [IntegerLiteral] 5
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 221| 3: [ExprStmt] <Expr>;
|
||||
# 221| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
|
||||
# 221| -1: [TypeAccess] TestKt
|
||||
# 221| 0: [StringLiteral] one-vararg-before sunk 3
|
||||
# 221| 1: [StringLiteral] one-vararg not sunk 3
|
||||
# 221| 2: [StringLiteral] one-vararg-after sunk 3
|
||||
# 222| 4: [ExprStmt] <Expr>;
|
||||
# 222| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
|
||||
# 222| -1: [TypeAccess] TestKt
|
||||
# 222| 0: [StringLiteral] two-varargs-before sunk 3
|
||||
# 222| 1: [StringLiteral] two-vararg-first not sunk 3
|
||||
# 222| 2: [StringLiteral] two-vararg-second not sunk 3
|
||||
# 222| 3: [StringLiteral] two-varargs-after sunk 3
|
||||
# 223| 5: [ExprStmt] <Expr>;
|
||||
# 223| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
|
||||
# 223| -1: [TypeAccess] TestKt
|
||||
# 223| 0: [StringLiteral] no-z-parmeter sunk 3
|
||||
# 223| 1: [ArrayCreationExpr] new String[]
|
||||
# 223| -2: [ArrayInit] {...}
|
||||
# 223| 0: [StringLiteral] no-z-parameter first vararg not sunk 3
|
||||
# 223| 1: [StringLiteral] no-z-parameter second vararg not sunk 3
|
||||
# 223| -1: [TypeAccess] String
|
||||
# 223| 0: [IntegerLiteral] 2
|
||||
# 1| 2: [NullLiteral] null
|
||||
# 1| 3: [IntegerLiteral] 3
|
||||
# 1| 4: [NullLiteral] null
|
||||
# 3| 2: [Class] TestMember
|
||||
# 3| 1: [Constructor] TestMember
|
||||
# 3| 5: [BlockStmt] { ... }
|
||||
|
||||
@@ -13,3 +13,6 @@
|
||||
| test.kt:171:3:171:97 | f | test.kt:171:3:171:97 | f$default |
|
||||
| test.kt:179:3:179:46 | f | test.kt:179:3:179:46 | f$default |
|
||||
| test.kt:180:3:180:34 | f | test.kt:180:3:180:34 | f$default |
|
||||
| test.kt:184:1:188:1 | varargsTest | test.kt:184:1:188:1 | varargsTest$default |
|
||||
| test.kt:199:1:201:1 | varargsTestOnlySinkVarargs | test.kt:199:1:201:1 | varargsTestOnlySinkVarargs$default |
|
||||
| test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs | test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs$default |
|
||||
|
||||
@@ -180,3 +180,45 @@ class TestOverloadsWithDefaults {
|
||||
fun f(z: String, w: Int = 0) { }
|
||||
|
||||
}
|
||||
|
||||
fun varargsTest(x: String = "before-vararg-default sunk", vararg y: String = arrayOf("first-vararg-default sunk", "second-vararg-default sunk"), z: String = "after-vararg-default sunk") {
|
||||
sink(x)
|
||||
sink(y[0])
|
||||
sink(z)
|
||||
}
|
||||
|
||||
fun varargsUser() {
|
||||
varargsTest()
|
||||
varargsTest(x = "no-varargs-before, no-z-parameter sunk")
|
||||
varargsTest(x = "no-varargs-before sunk", z = "no-varargs-after sunk")
|
||||
varargsTest(x = "one-vararg-before sunk", "one-vararg sunk", z = "one-vararg-after sunk")
|
||||
varargsTest(x = "two-varargs-before sunk", "two-vararg-first sunk", "two-vararg-second sunk", z = "two-varargs-after sunk")
|
||||
varargsTest("no-z-parmeter sunk", "no-z-parameter first vararg sunk", "no-z-parameter second vararg sunk")
|
||||
}
|
||||
|
||||
fun varargsTestOnlySinkVarargs(x: String = "before-vararg-default not sunk 2", vararg y: String = arrayOf("first-vararg-default sunk 2", "second-vararg-default sunk 2"), z: String = "after-vararg-default not sunk 2") {
|
||||
sink(y[0])
|
||||
}
|
||||
|
||||
fun varargsUserOnlySinkVarargs() {
|
||||
varargsTestOnlySinkVarargs()
|
||||
varargsTestOnlySinkVarargs(x = "no-varargs-before, no-z-parameter not sunk 2")
|
||||
varargsTestOnlySinkVarargs(x = "no-varargs-before not sunk 2", z = "no-varargs-after not sunk 2")
|
||||
varargsTestOnlySinkVarargs(x = "one-vararg-before not sunk 2", "one-vararg sunk 2", z = "one-vararg-after not sunk 2")
|
||||
varargsTestOnlySinkVarargs(x = "two-varargs-before not sunk 2", "two-vararg-first sunk 2", "two-vararg-second sunk 2", z = "two-varargs-after not sunk 2")
|
||||
varargsTestOnlySinkVarargs("no-z-parmeter not sunk 2", "no-z-parameter first vararg sunk 2", "no-z-parameter second vararg sunk 2")
|
||||
}
|
||||
|
||||
fun varargsTestOnlySinkRegularArgs(x: String = "before-vararg-default sunk 3", vararg y: String = arrayOf("first-vararg-default not sunk 3", "second-vararg-default not sunk 3"), z: String = "after-vararg-default sunk 3") {
|
||||
sink(x)
|
||||
sink(z)
|
||||
}
|
||||
|
||||
fun varargsUserOnlySinkRegularArgs() {
|
||||
varargsTestOnlySinkRegularArgs()
|
||||
varargsTestOnlySinkRegularArgs(x = "no-varargs-before, no-z-parameter sunk 3")
|
||||
varargsTestOnlySinkRegularArgs(x = "no-varargs-before sunk 3", z = "no-varargs-after sunk 3")
|
||||
varargsTestOnlySinkRegularArgs(x = "one-vararg-before sunk 3", "one-vararg not sunk 3", z = "one-vararg-after sunk 3")
|
||||
varargsTestOnlySinkRegularArgs(x = "two-varargs-before sunk 3", "two-vararg-first not sunk 3", "two-vararg-second not sunk 3", z = "two-varargs-after sunk 3")
|
||||
varargsTestOnlySinkRegularArgs("no-z-parmeter sunk 3", "no-z-parameter first vararg not sunk 3", "no-z-parameter second vararg not sunk 3")
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
| test.kt:36:40:36:41 | 30 | test.kt:25:10:25:18 | ...[...] |
|
||||
| test.kt:36:44:36:45 | 31 | test.kt:25:10:25:18 | ...[...] |
|
||||
| test.kt:36:48:36:49 | 32 | test.kt:25:10:25:18 | ...[...] |
|
||||
| test.kt:37:33:37:34 | 41 | test.kt:29:10:29:18 | ...[...] |
|
||||
| test.kt:37:37:37:38 | 42 | test.kt:29:10:29:18 | ...[...] |
|
||||
| test.kt:37:41:37:42 | 43 | test.kt:29:10:29:18 | ...[...] |
|
||||
| test.kt:41:26:41:27 | 51 | test.kt:9:10:9:18 | ...[...] |
|
||||
| test.kt:41:30:41:31 | 52 | test.kt:9:10:9:18 | ...[...] |
|
||||
| test.kt:41:34:41:35 | 53 | test.kt:9:10:9:18 | ...[...] |
|
||||
|
||||
24
java/ql/test/kotlin/query-tests/ConstantLoopCondition/A.kt
Normal file
24
java/ql/test/kotlin/query-tests/ConstantLoopCondition/A.kt
Normal file
@@ -0,0 +1,24 @@
|
||||
fun fn0(f: Function0<Unit>) = f()
|
||||
|
||||
fun fn1() {
|
||||
var c = true
|
||||
while (c) { // TODO: false positive
|
||||
fn0 {
|
||||
c = false
|
||||
}
|
||||
}
|
||||
|
||||
var d = true
|
||||
while (d) {
|
||||
fn0 {
|
||||
println(d)
|
||||
}
|
||||
}
|
||||
|
||||
val e = true
|
||||
while (e) {
|
||||
fn0 {
|
||||
println(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
| A.kt:5:12:5:12 | c | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:5:5:9:5 | while (...) | Loop |
|
||||
| A.kt:12:12:12:12 | d | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:12:5:16:5 | while (...) | Loop |
|
||||
| A.kt:19:12:19:12 | e | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:19:5:23:5 | while (...) | Loop |
|
||||
@@ -0,0 +1 @@
|
||||
Likely Bugs/Termination/ConstantLoopCondition.ql
|
||||
11
java/ql/test/kotlin/query-tests/UnderscoreIdentifier/Test.kt
Normal file
11
java/ql/test/kotlin/query-tests/UnderscoreIdentifier/Test.kt
Normal file
@@ -0,0 +1,11 @@
|
||||
fun fn() {
|
||||
try {
|
||||
val l = listOf(1, 2, 3)
|
||||
l.forEachIndexed { index, _ -> println(index) }
|
||||
|
||||
val p = Pair(1, 2)
|
||||
val (first, _) = p
|
||||
} catch (_: Exception) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Compatibility/JDK9/UnderscoreIdentifier.ql
|
||||
@@ -0,0 +1,7 @@
|
||||
| Test.kt:3:9:3:31 | List<Integer> l |
|
||||
| Test.kt:4:28:4:32 | index |
|
||||
| Test.kt:4:35:4:35 | p1 |
|
||||
| Test.kt:6:9:6:26 | Pair<Integer,Integer> p |
|
||||
| Test.kt:7:14:7:18 | int first |
|
||||
| Test.kt:7:26:7:26 | Pair<Integer,Integer> tmp0_container |
|
||||
| Test.kt:8:14:8:25 | Exception _ |
|
||||
@@ -0,0 +1,3 @@
|
||||
import java
|
||||
|
||||
query predicate variables(Variable v) { v.fromSource() }
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-all
|
||||
version: 0.3.2
|
||||
version: 0.3.3-dev
|
||||
groups: javascript
|
||||
dbscheme: semmlecode.javascript.dbscheme
|
||||
extractor: javascript
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/javascript-queries
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups:
|
||||
- javascript
|
||||
- queries
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
name: codeql/suite-helpers
|
||||
version: 0.3.2
|
||||
version: 0.3.3-dev
|
||||
groups: shared
|
||||
|
||||
11
python/.vscode/ql.code-snippets
vendored
11
python/.vscode/ql.code-snippets
vendored
@@ -25,6 +25,17 @@
|
||||
"description": "has relative path",
|
||||
},
|
||||
|
||||
|
||||
"debug based on location": {
|
||||
"scope": "ql",
|
||||
"prefix": "debug based on location",
|
||||
"body": [
|
||||
"$1.getLocation().getFile().getShortName() = \"$2\" and",
|
||||
"$1.getLocation().getStartLine() = $3 and",
|
||||
],
|
||||
"description": "debug based on location",
|
||||
},
|
||||
|
||||
"Exists": {
|
||||
"scope": "ql",
|
||||
"prefix": "exists",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-all
|
||||
version: 0.6.2
|
||||
version: 0.6.3-dev
|
||||
groups: python
|
||||
dbscheme: semmlecode.python.dbscheme
|
||||
extractor: python
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/python-queries
|
||||
version: 0.5.2
|
||||
version: 0.5.3-dev
|
||||
groups:
|
||||
- python
|
||||
- queries
|
||||
|
||||
4
ruby/ql/lib/change-notes/2022-10-21-local-taint-step.md
Normal file
4
ruby/ql/lib/change-notes/2022-10-21-local-taint-step.md
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* There was a bug in `TaintTracking::localTaint` and `TaintTracking::localTaintStep` such that they only tracked non-value-preserving flow steps. They have been fixed and now also include value-preserving steps.
|
||||
@@ -36,6 +36,13 @@ private module Cached {
|
||||
not s instanceof ModuleBase and
|
||||
result = getEnclosingMethod(s.getOuterScope())
|
||||
}
|
||||
|
||||
cached
|
||||
Toplevel getEnclosingToplevel(Scope s) {
|
||||
result = s
|
||||
or
|
||||
result = getEnclosingToplevel(s.getOuterScope())
|
||||
}
|
||||
}
|
||||
|
||||
private import Cached
|
||||
@@ -66,6 +73,9 @@ class AstNode extends TAstNode {
|
||||
/** Gets the enclosing method, if any. */
|
||||
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) }
|
||||
|
||||
/** Gets the enclosing top-level. */
|
||||
final Toplevel getEnclosingToplevel() { result = getEnclosingToplevel(scopeOfInclSynth(this)) }
|
||||
|
||||
/** Gets a textual representation of this node. */
|
||||
cached
|
||||
string toString() { none() }
|
||||
|
||||
@@ -271,10 +271,7 @@ module Http {
|
||||
|
||||
/** Gets the URL pattern for this route, if it can be statically determined. */
|
||||
string getUrlPattern() {
|
||||
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
|
||||
this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
|
||||
result = strNode.getExpr().getConstantValue().getStringlikeValue()
|
||||
)
|
||||
result = this.getUrlPatternArg().getALocalSource().getConstantValue().getStringlikeValue()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -538,10 +535,12 @@ module Http {
|
||||
|
||||
/** Gets the mimetype of this HTTP response, if it can be statically determined. */
|
||||
string getMimetype() {
|
||||
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
|
||||
this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and
|
||||
result = strNode.getExpr().getConstantValue().getStringlikeValue().splitAt(";", 0)
|
||||
)
|
||||
result =
|
||||
this.getMimetypeOrContentTypeArg()
|
||||
.getALocalSource()
|
||||
.getConstantValue()
|
||||
.getStringlikeValue()
|
||||
.splitAt(";", 0)
|
||||
or
|
||||
not exists(this.getMimetypeOrContentTypeArg()) and
|
||||
result = this.getMimetypeDefault()
|
||||
|
||||
@@ -170,6 +170,24 @@ module ConstantValue {
|
||||
|
||||
/** A constant `nil` value. */
|
||||
class ConstantNilValue extends ConstantValue, TNil { }
|
||||
|
||||
/** Gets the integer constant `x`. */
|
||||
ConstantValue fromInt(int x) { result.getInt() = x }
|
||||
|
||||
/** Gets the float constant `x`. */
|
||||
ConstantValue fromFloat(float x) { result.getFloat() = x }
|
||||
|
||||
/** Gets the string constant `x`. */
|
||||
ConstantValue fromString(string x) { result.getString() = x }
|
||||
|
||||
/** Gets the symbol constant `x`. */
|
||||
ConstantValue fromSymbol(string x) { result.getSymbol() = x }
|
||||
|
||||
/** Gets the regexp constant `x`. */
|
||||
ConstantValue fromRegExp(string x) { result.getRegExp() = x }
|
||||
|
||||
/** Gets the string, symbol, or regexp constant `x`. */
|
||||
ConstantValue fromStringlikeValue(string x) { result.getStringlikeValue() = x }
|
||||
}
|
||||
|
||||
/** An access to a constant. */
|
||||
@@ -252,6 +270,20 @@ private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAcces
|
||||
final override predicate hasGlobalScope() { value.matches("::%") }
|
||||
}
|
||||
|
||||
private class ConstantWriteAccessSynth extends ConstantAccess, TConstantWriteAccessSynth {
|
||||
private string value;
|
||||
|
||||
ConstantWriteAccessSynth() { this = TConstantWriteAccessSynth(_, _, value) }
|
||||
|
||||
final override string getName() {
|
||||
if this.hasGlobalScope() then result = value.suffix(2) else result = value
|
||||
}
|
||||
|
||||
final override Expr getScopeExpr() { synthChild(this, 0, result) }
|
||||
|
||||
final override predicate hasGlobalScope() { value.matches("::%") }
|
||||
}
|
||||
|
||||
/**
|
||||
* A use (read) of a constant.
|
||||
*
|
||||
@@ -323,7 +355,9 @@ class ConstantReadAccess extends ConstantAccess {
|
||||
*/
|
||||
class ConstantWriteAccess extends ConstantAccess {
|
||||
ConstantWriteAccess() {
|
||||
explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace
|
||||
explicitAssignmentNode(toGenerated(this), _) or
|
||||
this instanceof TNamespace or
|
||||
this instanceof TConstantWriteAccessSynth
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "ConstantWriteAccess" }
|
||||
|
||||
@@ -61,7 +61,7 @@ class ArgumentList extends Expr, TArgumentList {
|
||||
|
||||
private class LhsExpr_ =
|
||||
TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or
|
||||
TDestructuredLhsExpr;
|
||||
TDestructuredLhsExpr or TConstantWriteAccessSynth;
|
||||
|
||||
/**
|
||||
* A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of
|
||||
|
||||
@@ -3,6 +3,7 @@ private import codeql.ruby.CFG
|
||||
private import internal.AST
|
||||
private import internal.Module
|
||||
private import internal.TreeSitter
|
||||
private import internal.Scope
|
||||
|
||||
/**
|
||||
* A representation of a run-time `module` or `class` value.
|
||||
@@ -23,9 +24,30 @@ class Module extends TModule {
|
||||
/** Gets an `include`d module. */
|
||||
Module getAnIncludedModule() { result = getAnIncludedModule(this) }
|
||||
|
||||
/** Gets the super class or an included or prepended module. */
|
||||
Module getAnImmediateAncestor() {
|
||||
result = [this.getSuperClass(), this.getAPrependedModule(), this.getAnIncludedModule()]
|
||||
}
|
||||
|
||||
/** Gets a direct subclass or module including or prepending this one. */
|
||||
Module getAnImmediateDescendent() { this = result.getAnImmediateAncestor() }
|
||||
|
||||
/** Gets a module that is transitively subclassed, included, or prepended by this module. */
|
||||
pragma[inline]
|
||||
Module getAnAncestor() { result = this.getAnImmediateAncestor*() }
|
||||
|
||||
/** Gets a module that transitively subclasses, includes, or prepends this module. */
|
||||
pragma[inline]
|
||||
Module getADescendent() { result = this.getAnImmediateDescendent*() }
|
||||
|
||||
/** Holds if this module is a class. */
|
||||
pragma[noinline]
|
||||
predicate isClass() { this.getADeclaration() instanceof ClassDeclaration }
|
||||
predicate isClass() {
|
||||
this.getADeclaration() instanceof ClassDeclaration
|
||||
or
|
||||
// If another class extends this, but we can't see the class declaration, assume it's a class
|
||||
getSuperClass(_) = this
|
||||
}
|
||||
|
||||
/** Gets a textual representation of this module. */
|
||||
string toString() {
|
||||
@@ -58,6 +80,99 @@ class Module extends TModule {
|
||||
loc.getStartColumn()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets a constant or `self` access that refers to this module. */
|
||||
private Expr getAnImmediateReferenceBase() {
|
||||
resolveConstantReadAccess(result) = this
|
||||
or
|
||||
result.(SelfVariableAccess).getVariable() = this.getADeclaration().getModuleSelfVariable()
|
||||
}
|
||||
|
||||
/** Gets a singleton class that augments this module object. */
|
||||
SingletonClass getASingletonClass() { result.getValue() = this.getAnImmediateReferenceBase() }
|
||||
|
||||
/**
|
||||
* Gets a singleton method on this module, either declared as a singleton method
|
||||
* or an instance method on a singleton class.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
MethodBase getAnOwnSingletonMethod() {
|
||||
result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
|
||||
or
|
||||
result = this.getASingletonClass().getAMethod().(Method)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance method named `name` declared in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
Method getOwnInstanceMethod(string name) { result = this.getADeclaration().getMethod(name) }
|
||||
|
||||
/**
|
||||
* Gets an instance method declared in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
Method getAnOwnInstanceMethod() { result = this.getADeclaration().getMethod(_) }
|
||||
|
||||
/**
|
||||
* Gets the instance method named `name` available in this module, including methods inherited
|
||||
* from ancestors.
|
||||
*/
|
||||
Method getInstanceMethod(string name) { result = lookupMethod(this, name) }
|
||||
|
||||
/**
|
||||
* Gets an instance method available in this module, including methods inherited
|
||||
* from ancestors.
|
||||
*/
|
||||
Method getAnInstanceMethod() { result = lookupMethod(this, _) }
|
||||
|
||||
/** Gets a constant or `self` access that refers to this module. */
|
||||
Expr getAnImmediateReference() {
|
||||
result = this.getAnImmediateReferenceBase()
|
||||
or
|
||||
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string getEnclosingModuleName() {
|
||||
exists(string qname |
|
||||
qname = this.getQualifiedName() and
|
||||
result = qname.regexpReplaceAll("::[^:]*$", "") and
|
||||
qname != result
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string getOwnModuleName() {
|
||||
result = this.getQualifiedName().regexpReplaceAll("^.*::", "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the enclosing module, as it appears in the qualified name of this module.
|
||||
*
|
||||
* For example, the parent module of `A::B` is `A`, and `A` itself has no parent module.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Module getParentModule() { result.getQualifiedName() = this.getEnclosingModuleName() }
|
||||
|
||||
/**
|
||||
* Gets a module named `name` declared inside this one (not aliased), provided
|
||||
* that such a module is defined or reopened in the current codebase.
|
||||
*
|
||||
* For example, for `A::B` the nested module named `C` would be `A::B::C`.
|
||||
*
|
||||
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
|
||||
* module whose qualified name is not `A::B::C`, then it will not be found by
|
||||
* this predicate.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
Module getNestedModule(string name) {
|
||||
result.getParentModule() = this and
|
||||
result.getOwnModuleName() = name
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -136,6 +251,46 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
|
||||
|
||||
/** Gets the representation of the run-time value of this module or class. */
|
||||
Module getModule() { none() }
|
||||
|
||||
/**
|
||||
* Gets the `self` variable in the module-level scope.
|
||||
*
|
||||
* Does not include the `self` variable from any of the methods in the module.
|
||||
*/
|
||||
SelfVariable getModuleSelfVariable() { result.getDeclaringScope() = this }
|
||||
|
||||
/** Gets the nearest enclosing `Namespace` or `Toplevel`, possibly this module itself. */
|
||||
Namespace getNamespaceOrToplevel() {
|
||||
result = this
|
||||
or
|
||||
not this instanceof Namespace and
|
||||
result = this.getEnclosingModule().getNamespaceOrToplevel()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an expression denoting the super class or an included or prepended module.
|
||||
*
|
||||
* For example, `C` is an ancestor expression of `M` in each of the following examples:
|
||||
* ```rb
|
||||
* class M < C
|
||||
* end
|
||||
*
|
||||
* module M
|
||||
* include C
|
||||
* prepend C
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
Expr getAnAncestorExpr() {
|
||||
exists(MethodCall call |
|
||||
call.getReceiver().(SelfVariableAccess).getVariable() = this.getModuleSelfVariable() and
|
||||
call.getMethodName() = ["include", "prepend"] and
|
||||
result = call.getArgument(0) and
|
||||
scopeOfInclSynth(call) = this // only permit calls directly in the module scope, not in a block
|
||||
)
|
||||
or
|
||||
result = this.(ClassDeclaration).getSuperclassExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -116,6 +116,9 @@ private module Cached {
|
||||
TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) {
|
||||
mkSynthChild(ConstantReadAccessKind(value), parent, i)
|
||||
} or
|
||||
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
|
||||
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
|
||||
} or
|
||||
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
|
||||
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
|
||||
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
|
||||
@@ -373,12 +376,13 @@ private module Cached {
|
||||
class TAstNodeSynth =
|
||||
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
|
||||
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
|
||||
TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or
|
||||
TGlobalVariableAccessSynth or TIfSynth or TInstanceVariableAccessSynth or
|
||||
TIntegerLiteralSynth or TLShiftExprSynth or TLocalVariableAccessSynth or
|
||||
TLogicalAndExprSynth or TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or
|
||||
TMulExprSynth or TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or
|
||||
TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
|
||||
TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or
|
||||
TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or
|
||||
TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or
|
||||
TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or
|
||||
TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or
|
||||
TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
|
||||
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
|
||||
|
||||
/**
|
||||
* Gets the underlying TreeSitter entity for a given AST node. This does not
|
||||
@@ -565,6 +569,8 @@ private module Cached {
|
||||
or
|
||||
result = TConstantReadAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TConstantWriteAccessSynth(parent, i, _)
|
||||
or
|
||||
result = TDivExprSynth(parent, i)
|
||||
or
|
||||
result = TExponentExprSynth(parent, i)
|
||||
@@ -672,7 +678,8 @@ class TMethodCall =
|
||||
class TSuperCall = TTokenSuperCall or TRegularSuperCall;
|
||||
|
||||
class TConstantAccess =
|
||||
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth;
|
||||
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or
|
||||
TConstantReadAccessSynth or TConstantWriteAccessSynth;
|
||||
|
||||
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
private import codeql.ruby.AST
|
||||
private import Scope as Scope
|
||||
|
||||
// Names of built-in modules and classes
|
||||
private string builtin() {
|
||||
result =
|
||||
[
|
||||
"Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass",
|
||||
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "Symbol", "Proc"
|
||||
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "String", "Symbol",
|
||||
"Proc",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,6 +18,8 @@ private module Cached {
|
||||
TResolved(string qName) {
|
||||
qName = builtin()
|
||||
or
|
||||
qName = getAnAssumedGlobalConst()
|
||||
or
|
||||
qName = namespaceDeclaration(_)
|
||||
} or
|
||||
TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) }
|
||||
@@ -38,7 +42,10 @@ private module Cached {
|
||||
Module getSuperClass(Module cls) {
|
||||
cls = TResolved("Object") and result = TResolved("BasicObject")
|
||||
or
|
||||
cls = TResolved(["Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass"]) and
|
||||
cls =
|
||||
TResolved([
|
||||
"Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass", "String"
|
||||
]) and
|
||||
result = TResolved("Object")
|
||||
or
|
||||
cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and
|
||||
@@ -58,6 +65,12 @@ private module Cached {
|
||||
forex(ClassDeclaration d | d = cls.getADeclaration() |
|
||||
not exists(resolveConstantReadAccess(d.getSuperclassExpr()))
|
||||
)
|
||||
or
|
||||
// If a module is used as a base class of another class, but we don't see its class declaration
|
||||
// treat it as a class extending Object, so its subclasses transitively extend Object.
|
||||
result = TResolved("Object") and
|
||||
not cls.getADeclaration() instanceof ClassDeclaration and
|
||||
cls = resolveConstantReadAccess(any(ClassDeclaration d).getSuperclassExpr())
|
||||
)
|
||||
}
|
||||
|
||||
@@ -65,7 +78,7 @@ private module Cached {
|
||||
(
|
||||
m = resolveConstantReadAccess(c.getReceiver())
|
||||
or
|
||||
m = enclosingModule(c).getModule() and
|
||||
m = enclosingModuleNoBlock(c).getModule() and
|
||||
c.getReceiver() instanceof SelfVariableAccess
|
||||
) and
|
||||
result = resolveConstantReadAccess(c.getAnArgument())
|
||||
@@ -388,11 +401,23 @@ private module ResolveImpl {
|
||||
result = resolveConstantWriteAccessRec(c, _, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of a constant `C` that we assume to be defined in the top-level because
|
||||
* it is referenced in a way that can only resolve to a top-level constant.
|
||||
*/
|
||||
string getAnAssumedGlobalConst() {
|
||||
exists(ConstantAccess access |
|
||||
not exists(access.getScopeExpr()) and
|
||||
result = access.getName() and
|
||||
isToplevel(access)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private string isDefinedConstantNonRec(string container, string name) {
|
||||
result = resolveConstantWriteAccessNonRec(_, container, name)
|
||||
or
|
||||
result = builtin() and
|
||||
result = [builtin(), getAnAssumedGlobalConst()] and
|
||||
name = result and
|
||||
container = "Object"
|
||||
}
|
||||
@@ -447,7 +472,7 @@ private module ResolveImpl {
|
||||
result = resolveConstantReadAccess(this.getReceiver(), _)
|
||||
or
|
||||
exists(ModuleBase encl |
|
||||
encl = enclosingModule(this) and
|
||||
encl = enclosingModuleNoBlock(this) and
|
||||
result = [qualifiedModuleNameNonRec(encl, _, _), qualifiedModuleNameRec(encl, _, _)]
|
||||
|
|
||||
this.getReceiver() instanceof SelfVariableAccess
|
||||
@@ -495,7 +520,20 @@ private module ResolveImpl {
|
||||
private import ResolveImpl
|
||||
|
||||
/**
|
||||
* A variant of AstNode::getEnclosingModule that excludes
|
||||
* Gets an enclosing scope of `scope`, stopping at the first module or block.
|
||||
*
|
||||
* Includes `scope` itself and the final module/block.
|
||||
*/
|
||||
private Scope enclosingScopesNoBlock(Scope scope) {
|
||||
result = scope
|
||||
or
|
||||
not scope instanceof ModuleBase and
|
||||
not scope instanceof Block and
|
||||
result = enclosingScopesNoBlock(scope.getOuterScope())
|
||||
}
|
||||
|
||||
/**
|
||||
* A variant of `AstNode::getEnclosingModule` that excludes
|
||||
* results that are enclosed in a block. This is a bit wrong because
|
||||
* it could lead to false negatives. However, `include` statements in
|
||||
* blocks are very rare in normal code. The majority of cases are in calls
|
||||
@@ -503,15 +541,10 @@ private import ResolveImpl
|
||||
* methods evaluate the block in the context of some other module/class instead of
|
||||
* the enclosing one.
|
||||
*/
|
||||
private ModuleBase enclosingModule(AstNode node) {
|
||||
result = node.getParent()
|
||||
or
|
||||
exists(AstNode mid |
|
||||
result = enclosingModule(mid) and
|
||||
mid = node.getParent() and
|
||||
not mid instanceof ModuleBase and
|
||||
not mid instanceof Block
|
||||
)
|
||||
private ModuleBase enclosingModuleNoBlock(Stmt node) {
|
||||
// Note: don't rely on AstNode.getParent() here.
|
||||
// Instead use Scope.getOuterScope() to correctly handle the scoping of things like Namespace.getScopeExpr().
|
||||
result = enclosingScopesNoBlock(Scope::scopeOfInclSynth(node))
|
||||
}
|
||||
|
||||
private Module getAncestors(Module m) {
|
||||
|
||||
@@ -42,7 +42,8 @@ newtype SynthKind =
|
||||
StmtSequenceKind() or
|
||||
SelfKind(SelfVariable v) or
|
||||
SubExprKind() or
|
||||
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) }
|
||||
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or
|
||||
ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) }
|
||||
|
||||
/**
|
||||
* An AST child.
|
||||
@@ -107,6 +108,11 @@ class Synthesis extends TSynthesis {
|
||||
*/
|
||||
predicate constantReadAccess(string name) { none() }
|
||||
|
||||
/**
|
||||
* Holds if a constant write access of `name` is needed.
|
||||
*/
|
||||
predicate constantWriteAccess(string name) { none() }
|
||||
|
||||
/**
|
||||
* Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
|
||||
*/
|
||||
@@ -493,6 +499,231 @@ private module AssignOperationDesugar {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment operation where the left-hand side is a constant
|
||||
* without scope expression, such as`FOO` or `::Foo`.
|
||||
*/
|
||||
private class ConstantAssignOperation extends AssignOperation {
|
||||
string name;
|
||||
|
||||
pragma[nomagic]
|
||||
ConstantAssignOperation() {
|
||||
name =
|
||||
any(Ruby::Constant constant | TTokenConstantAccess(constant) = this.getLeftOperand())
|
||||
.getValue()
|
||||
or
|
||||
name =
|
||||
"::" +
|
||||
any(Ruby::Constant constant |
|
||||
TScopeResolutionConstantAccess(any(Ruby::ScopeResolution g | not exists(g.getScope())),
|
||||
constant) = this.getLeftOperand()
|
||||
).getValue()
|
||||
}
|
||||
|
||||
final string getName() { result = name }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate constantAssignOperationSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(ConstantAssignOperation cao |
|
||||
parent = cao and
|
||||
i = -1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(cao, -1) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = childRef(cao.getLeftOperand())
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = SynthChild(getKind(cao))
|
||||
or
|
||||
parent = getSynthChild(assign, 1) and
|
||||
(
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind(cao.getName()))
|
||||
or
|
||||
i = 1 and
|
||||
child = childRef(cao.getRightOperand())
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* FOO += y
|
||||
* ```
|
||||
*
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* FOO = FOO + y
|
||||
* ```
|
||||
*/
|
||||
private class ConstantAssignOperationSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
constantAssignOperationSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) {
|
||||
name = any(ConstantAssignOperation o).getName()
|
||||
}
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(ConstantAssignOperation cao, BinaryOperation bo |
|
||||
bo = cao.getDesugared().(AssignExpr).getRightOperand()
|
||||
|
|
||||
n = bo and
|
||||
l = getAssignOperationLocation(cao)
|
||||
or
|
||||
n = bo.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An assignment operation where the left-hand side is a constant
|
||||
* with scope expression, such as `expr::FOO`.
|
||||
*/
|
||||
private class ScopeResolutionAssignOperation extends AssignOperation {
|
||||
string name;
|
||||
Expr scope;
|
||||
|
||||
pragma[nomagic]
|
||||
ScopeResolutionAssignOperation() {
|
||||
exists(Ruby::Constant constant, Ruby::ScopeResolution g |
|
||||
TScopeResolutionConstantAccess(g, constant) = this.getLeftOperand() and
|
||||
name = constant.getValue() and
|
||||
toGenerated(scope) = g.getScope()
|
||||
)
|
||||
}
|
||||
|
||||
final string getName() { result = name }
|
||||
|
||||
final Expr getScopeExpr() { result = scope }
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate scopeResolutionAssignOperationSynthesis(AstNode parent, int i, Child child) {
|
||||
exists(ScopeResolutionAssignOperation cao |
|
||||
parent = cao and
|
||||
i = -1 and
|
||||
child = SynthChild(StmtSequenceKind())
|
||||
or
|
||||
exists(AstNode stmts | stmts = TStmtSequenceSynth(cao, -1) |
|
||||
parent = stmts and
|
||||
i = 0 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(stmts, 0) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = childRef(cao.getScopeExpr())
|
||||
)
|
||||
or
|
||||
parent = stmts and
|
||||
i = 1 and
|
||||
child = SynthChild(AssignExprKind())
|
||||
or
|
||||
exists(AstNode assign | assign = TAssignExprSynth(stmts, 1) |
|
||||
parent = assign and
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantWriteAccessKind(cao.getName()))
|
||||
or
|
||||
exists(AstNode cwa | cwa = TConstantWriteAccessSynth(assign, 0, cao.getName()) |
|
||||
parent = cwa and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
)
|
||||
or
|
||||
parent = assign and
|
||||
i = 1 and
|
||||
child = SynthChild(getKind(cao))
|
||||
or
|
||||
exists(AstNode op | op = getSynthChild(assign, 1) |
|
||||
parent = op and
|
||||
i = 0 and
|
||||
child = SynthChild(ConstantReadAccessKind(cao.getName()))
|
||||
or
|
||||
exists(AstNode cra | cra = TConstantReadAccessSynth(op, 0, cao.getName()) |
|
||||
parent = cra and
|
||||
i = 0 and
|
||||
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
|
||||
)
|
||||
or
|
||||
parent = op and
|
||||
i = 1 and
|
||||
child = childRef(cao.getRightOperand())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* ```rb
|
||||
* expr::FOO += y
|
||||
* ```
|
||||
*
|
||||
* desugars to
|
||||
*
|
||||
* ```rb
|
||||
* __synth__0 = expr
|
||||
* __synth__0::FOO = _synth__0::FOO + y
|
||||
* ```
|
||||
*/
|
||||
private class ScopeResolutionAssignOperationSynthesis extends Synthesis {
|
||||
final override predicate child(AstNode parent, int i, Child child) {
|
||||
scopeResolutionAssignOperationSynthesis(parent, i, child)
|
||||
}
|
||||
|
||||
final override predicate constantReadAccess(string name) {
|
||||
name = any(ScopeResolutionAssignOperation o).getName()
|
||||
}
|
||||
|
||||
final override predicate localVariable(AstNode n, int i) {
|
||||
n instanceof ScopeResolutionAssignOperation and
|
||||
i = 0
|
||||
}
|
||||
|
||||
final override predicate constantWriteAccess(string name) { this.constantReadAccess(name) }
|
||||
|
||||
final override predicate location(AstNode n, Location l) {
|
||||
exists(ScopeResolutionAssignOperation cao, StmtSequence stmts | stmts = cao.getDesugared() |
|
||||
n = stmts.getStmt(0) and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
or
|
||||
exists(AssignExpr assign | assign = stmts.getStmt(1) |
|
||||
n = assign and hasLocation(cao, l)
|
||||
or
|
||||
n = assign.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
or
|
||||
n = assign.getLeftOperand().(ConstantAccess).getScopeExpr() and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
or
|
||||
exists(BinaryOperation bo | bo = assign.getRightOperand() |
|
||||
n = bo and
|
||||
l = getAssignOperationLocation(cao)
|
||||
or
|
||||
n = bo.getLeftOperand() and
|
||||
hasLocation(cao.getLeftOperand(), l)
|
||||
or
|
||||
n = bo.getLeftOperand().(ConstantAccess).getScopeExpr() and
|
||||
hasLocation(cao.getScopeExpr(), l)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** An assignment operation where the left-hand side is a method call. */
|
||||
private class SetterAssignOperation extends AssignOperation {
|
||||
private MethodCall mc;
|
||||
|
||||
@@ -7,7 +7,14 @@ private import internal.ControlFlowGraphImpl
|
||||
private import internal.Splitting
|
||||
private import internal.Completion
|
||||
|
||||
/** An AST node with an associated control-flow graph. */
|
||||
/**
|
||||
* An AST node with an associated control-flow graph.
|
||||
*
|
||||
* Top-levels, methods, blocks, and lambdas are all CFG scopes.
|
||||
*
|
||||
* Note that module declarations are not themselves CFG scopes, as they are part of
|
||||
* the CFG of the enclosing top-level or callable.
|
||||
*/
|
||||
class CfgScope extends Scope instanceof CfgScopeImpl {
|
||||
/** Gets the CFG scope that this scope is nested under, if any. */
|
||||
final CfgScope getOuterCfgScope() {
|
||||
|
||||
@@ -890,7 +890,12 @@ module Trees {
|
||||
private class ConstantAccessTree extends PostOrderTree, ConstantAccess {
|
||||
ConstantAccessTree() {
|
||||
not this instanceof ClassDeclaration and
|
||||
not this instanceof ModuleDeclaration
|
||||
not this instanceof ModuleDeclaration and
|
||||
// constant accesses with scope expression in compound assignments are desugared
|
||||
not (
|
||||
this = any(AssignOperation op).getLeftOperand() and
|
||||
exists(this.getScopeExpr())
|
||||
)
|
||||
}
|
||||
|
||||
final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() }
|
||||
|
||||
@@ -289,7 +289,7 @@ module Ssa {
|
||||
)
|
||||
}
|
||||
|
||||
final override string toString() { result = "<captured>" }
|
||||
final override string toString() { result = "<captured> " + this.getSourceVariable() }
|
||||
|
||||
override Location getLocation() { result = this.getBasicBlock().getLocation() }
|
||||
}
|
||||
|
||||
@@ -375,17 +375,10 @@ private module Cached {
|
||||
private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
|
||||
RelevantCall call, Module m, string method
|
||||
) {
|
||||
exists(SsaSelfDefinitionNode self, Module target, MethodBase caller |
|
||||
exists(SsaSelfDefinitionNode self, MethodBase caller |
|
||||
flowsToMethodCallReceiver(call, self, method) and
|
||||
target = m.getSuperClass*() and
|
||||
selfInMethod(self.getVariable(), caller, target) and
|
||||
singletonMethod(caller, _, _) and
|
||||
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
|
||||
// methods on Object, if the block is actually evaluated in the context of another class.
|
||||
// The 'self' inside such a singleton method could then be any class, leading to self-calls
|
||||
// being resolved to arbitrary singleton methods.
|
||||
// To remedy this, we do not allow following super-classes all the way to Object.
|
||||
not (m != target and target = TResolved("Object"))
|
||||
selfInMethod(self.getVariable(), caller, m) and
|
||||
singletonMethod(caller, _, _)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -441,12 +434,13 @@ private module Cached {
|
||||
// M.extend(M)
|
||||
// M.instance # <- call
|
||||
// ```
|
||||
exists(Module m | result = lookupSingletonMethod(m, method) |
|
||||
exists(Module m, boolean exact | result = lookupSingletonMethod(m, method, exact) |
|
||||
// ```rb
|
||||
// def C.singleton; end # <- result
|
||||
// C.singleton # <- call
|
||||
// ```
|
||||
moduleFlowsToMethodCallReceiver(call, m, method)
|
||||
moduleFlowsToMethodCallReceiver(call, m, method) and
|
||||
exact = true
|
||||
or
|
||||
// ```rb
|
||||
// class C
|
||||
@@ -454,7 +448,8 @@ private module Cached {
|
||||
// self.singleton # <- call
|
||||
// end
|
||||
// ```
|
||||
selfInModuleFlowsToMethodCallReceiver(call, m, method)
|
||||
selfInModuleFlowsToMethodCallReceiver(call, m, method) and
|
||||
exact = true
|
||||
or
|
||||
// ```rb
|
||||
// class C
|
||||
@@ -464,7 +459,8 @@ private module Cached {
|
||||
// end
|
||||
// end
|
||||
// ```
|
||||
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method)
|
||||
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method) and
|
||||
exact = false
|
||||
)
|
||||
)
|
||||
or
|
||||
@@ -796,26 +792,54 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private MethodBase lookupSingletonMethodDirect(Module m, string name) {
|
||||
singletonMethodOnModule(result, name, m)
|
||||
or
|
||||
exists(DataFlow::LocalSourceNode sourceNode |
|
||||
sourceNode = trackModuleAccess(m) and
|
||||
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
|
||||
flowsToSingletonMethodObject(sourceNode, result, name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a singleton method named `name`, defined on module
|
||||
* `m`, or any transitive base class of `m`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
private MethodBase lookupSingletonMethod(Module m, string name) {
|
||||
singletonMethodOnModule(result, name, m)
|
||||
or
|
||||
// cannot be part of `singletonMethodOnModule` because it would introduce
|
||||
// negative recursion below
|
||||
exists(DataFlow::LocalSourceNode sourceNode |
|
||||
sourceNode = trackModuleAccess(m) and
|
||||
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
|
||||
flowsToSingletonMethodObject(sourceNode, result, name)
|
||||
)
|
||||
result = lookupSingletonMethodDirect(m, name)
|
||||
or
|
||||
// cannot use `lookupSingletonMethodDirect` because it would introduce
|
||||
// negative recursion
|
||||
not singletonMethodOnModule(_, name, m) and
|
||||
result = lookupSingletonMethod(m.getSuperClass(), name)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private MethodBase lookupSingletonMethodInSubClasses(Module m, string name) {
|
||||
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
|
||||
// methods on Object, if the block is actually evaluated in the context of another class.
|
||||
// The 'self' inside such a singleton method could then be any class, leading to self-calls
|
||||
// being resolved to arbitrary singleton methods.
|
||||
// To remedy this, we do not allow following super-classes all the way to Object.
|
||||
not m = TResolved("Object") and
|
||||
exists(Module sub | sub.getSuperClass() = m |
|
||||
result = lookupSingletonMethodDirect(sub, name) or
|
||||
result = lookupSingletonMethodInSubClasses(sub, name)
|
||||
)
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private MethodBase lookupSingletonMethod(Module m, string name, boolean exact) {
|
||||
result = lookupSingletonMethod(m, name) and
|
||||
exact in [false, true]
|
||||
or
|
||||
result = lookupSingletonMethodInSubClasses(m, name) and
|
||||
exact = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `method` is a singleton method named `name`, defined on expression
|
||||
* `object`, where `object` is not likely to resolve to a module:
|
||||
|
||||
@@ -109,11 +109,27 @@ module LocalFlow {
|
||||
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
|
||||
*/
|
||||
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
|
||||
nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter())
|
||||
nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter())
|
||||
or
|
||||
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod())
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `nodeFrom -> nodeTo` is a step from a parameter to a capture entry node for
|
||||
* that parameter.
|
||||
*
|
||||
* This is intended to recover from flow not currently recognised by ordinary capture flow.
|
||||
*/
|
||||
predicate localFlowSsaParamCaptureInput(Node nodeFrom, Node nodeTo) {
|
||||
exists(Ssa::CapturedEntryDefinition def |
|
||||
nodeFrom.asParameter().(NamedParameter).getVariable() = def.getSourceVariable()
|
||||
or
|
||||
nodeFrom.(SelfParameterNode).getSelfVariable() = def.getSourceVariable()
|
||||
|
|
||||
nodeTo.(SsaDefinitionNode).getDefinition() = def
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
|
||||
* involving SSA definition `def`.
|
||||
@@ -246,6 +262,9 @@ private module Cached {
|
||||
)
|
||||
} or
|
||||
TSsaDefinitionNode(Ssa::Definition def) or
|
||||
TRawNamespaceSelf(Namespace ns) {
|
||||
not exists(Ssa::SelfDefinition def | def.getSourceVariable() = ns.getModuleSelfVariable())
|
||||
} or
|
||||
TNormalParameterNode(Parameter p) {
|
||||
p instanceof SimpleParameter or
|
||||
p instanceof OptionalParameter or
|
||||
@@ -299,7 +318,7 @@ private module Cached {
|
||||
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
|
||||
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
|
||||
or
|
||||
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
|
||||
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
|
||||
or
|
||||
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
|
||||
or
|
||||
@@ -316,7 +335,7 @@ private module Cached {
|
||||
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
|
||||
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
|
||||
or
|
||||
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom)
|
||||
defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
|
||||
or
|
||||
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
|
||||
or
|
||||
@@ -327,7 +346,12 @@ private module Cached {
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _)
|
||||
}
|
||||
|
||||
/** This is the local flow predicate that is used in type tracking. */
|
||||
/**
|
||||
* This is the local flow predicate that is used in type tracking.
|
||||
*
|
||||
* This needs to exclude `localFlowSsaParamInput` due to a performance trick
|
||||
* in type tracking, where such steps are treated as call steps.
|
||||
*/
|
||||
cached
|
||||
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
|
||||
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
|
||||
@@ -366,7 +390,7 @@ private module Cached {
|
||||
|
||||
cached
|
||||
predicate isLocalSourceNode(Node n) {
|
||||
n instanceof ParameterNode
|
||||
n instanceof TParameterNode
|
||||
or
|
||||
// Expressions that can't be reached from another entry definition or expression
|
||||
n instanceof ExprNode and
|
||||
@@ -381,6 +405,8 @@ private module Cached {
|
||||
or
|
||||
// Needed for stores in type tracking
|
||||
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
|
||||
or
|
||||
n instanceof TRawNamespaceSelf
|
||||
}
|
||||
|
||||
cached
|
||||
@@ -502,6 +528,38 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionNode {
|
||||
Scope getSelfScope() { result = self.getDeclaringScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A representative for the created or re-opened class/module in a `Namespace` that does
|
||||
* not have an SSA definition for `self`.
|
||||
*/
|
||||
class RawNamespaceSelf extends NodeImpl, TRawNamespaceSelf {
|
||||
private Namespace namespace;
|
||||
|
||||
RawNamespaceSelf() { this = TRawNamespaceSelf(namespace) }
|
||||
|
||||
/** Gets the namespace for which this represents the created or re-opened class/module. */
|
||||
Namespace getNamespace() { result = namespace }
|
||||
|
||||
override CfgScope getCfgScope() { result = namespace.getCfgScope() }
|
||||
|
||||
override Location getLocationImpl() { result = namespace.getLocation() }
|
||||
|
||||
override string toStringImpl() { result = namespace.toString() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a representative for the created or re-opened class/module in a `Namespace`.
|
||||
*
|
||||
* This is usually the SSA definition for `self`, but if this node does not exist due to lack of liveness,
|
||||
* it is the `RawNamespaceSelf` node.
|
||||
*/
|
||||
LocalSourceNode getNamespaceSelf(Namespace namespace) {
|
||||
result.(SsaDefinitionNode).getDefinition().(Ssa::SelfDefinition).getSourceVariable() =
|
||||
namespace.getModuleSelfVariable()
|
||||
or
|
||||
result = TRawNamespaceSelf(namespace)
|
||||
}
|
||||
|
||||
/**
|
||||
* A value returning statement, viewed as a node in a data flow graph.
|
||||
*
|
||||
@@ -1242,7 +1300,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
|
||||
creation.asExpr() =
|
||||
any(CfgNodes::ExprNodes::MethodCallCfgNode mc |
|
||||
c.asCallable() = mc.getBlock().getExpr() and
|
||||
mc.getExpr().getMethodName() = "lambda"
|
||||
mc.getExpr().getMethodName() = ["lambda", "proc"]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -54,6 +54,30 @@ class Node extends TNode {
|
||||
* Gets a data flow node to which data may flow from this node in one local step.
|
||||
*/
|
||||
Node getASuccessor() { localFlowStep(this, result) }
|
||||
|
||||
/** Gets the constant value of this expression, if any. */
|
||||
ConstantValue getConstantValue() { result = this.asExpr().getConstantValue() }
|
||||
|
||||
/**
|
||||
* Gets the callable corresponding to this block, lambda expression, or call to `proc` or `lambda`.
|
||||
*
|
||||
* For example, gets the callable in each of the following cases:
|
||||
* ```rb
|
||||
* { |x| x } # block expression
|
||||
* ->(x) { x } # lambda expression
|
||||
* proc { |x| x } # call to 'proc'
|
||||
* lambda { |x| x } # call to 'lambda'
|
||||
* ```
|
||||
*/
|
||||
pragma[noinline]
|
||||
CallableNode asCallable() {
|
||||
result = this
|
||||
or
|
||||
exists(DataFlowCallable c |
|
||||
lambdaCreation(this, _, c) and
|
||||
result.asCallableAstNode() = c.asCallable()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/** A data-flow node corresponding to a call in the control-flow graph. */
|
||||
@@ -124,6 +148,31 @@ class CallNode extends LocalSourceNode, ExprNode {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a setter method.
|
||||
*
|
||||
* For example,
|
||||
* ```rb
|
||||
* self.foo = 10
|
||||
* a[0] = 10
|
||||
* ```
|
||||
*/
|
||||
class SetterCallNode extends CallNode {
|
||||
SetterCallNode() { this.getExprNode().getExpr() instanceof SetterMethodCall }
|
||||
|
||||
/**
|
||||
* Gets the name of the method being called without the trailing `=`. For example, in the following
|
||||
* two statements the target name is `value`:
|
||||
* ```rb
|
||||
* foo.value=(1)
|
||||
* foo.value = 1
|
||||
* ```
|
||||
*/
|
||||
final string getTargetName() {
|
||||
result = this.getExprNode().getExpr().(SetterMethodCall).getTargetName()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An expression, viewed as a node in a data flow graph.
|
||||
*
|
||||
@@ -144,7 +193,7 @@ class ExprNode extends Node, TExprNode {
|
||||
* The value of a parameter at function entry, viewed as a node in a data
|
||||
* flow graph.
|
||||
*/
|
||||
class ParameterNode extends Node, TParameterNode instanceof ParameterNodeImpl {
|
||||
class ParameterNode extends LocalSourceNode, TParameterNode instanceof ParameterNodeImpl {
|
||||
/** Gets the parameter corresponding to this node, if any. */
|
||||
final Parameter getParameter() { result = super.getParameter() }
|
||||
}
|
||||
@@ -174,6 +223,80 @@ class LocalSourceNode extends Node {
|
||||
*/
|
||||
pragma[inline]
|
||||
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
|
||||
|
||||
/**
|
||||
* Gets a node to which data may flow from this node in zero or
|
||||
* more local data-flow steps.
|
||||
*/
|
||||
pragma[inline]
|
||||
Node getALocalUse() { hasLocalSource(result, this) }
|
||||
|
||||
/** Gets a method call where this node flows to the receiver. */
|
||||
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
|
||||
|
||||
/** Gets a call to a method named `name`, where this node flows to the receiver. */
|
||||
CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
|
||||
|
||||
/** Gets a call `obj.name` with no arguments, where this node flows to `obj`. */
|
||||
CallNode getAnAttributeRead(string name) {
|
||||
result = this.getAMethodCall(name) and
|
||||
result.getNumberOfArguments() = 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value assigned to `name` on this object, such as the `x` in `obj.name = x`.
|
||||
*
|
||||
* Concretely, this gets the argument of any call to `name=` where this node flows to the receiver.
|
||||
*/
|
||||
Node getAnAttributeWriteValue(string name) {
|
||||
result = this.getAMethodCall(name + "=").getArgument(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to an element on this node, such as `obj[key]`.
|
||||
*
|
||||
* Concretely this gets a call to `[]` with 1 argument, where this node flows to the receiver.
|
||||
*/
|
||||
CallNode getAnElementRead() {
|
||||
result = this.getAMethodCall("[]") and result.getNumberOfArguments() = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the element `key` on this node, such as `obj[:key]`.
|
||||
*
|
||||
* Concretely this gets a call to `[]` where this node flows to the receiver
|
||||
* and the first and only argument has the constant value `key`.
|
||||
*/
|
||||
CallNode getAnElementRead(ConstantValue key) {
|
||||
result = this.getAnElementRead() and
|
||||
key = result.getArgument(0).getConstantValue()
|
||||
}
|
||||
|
||||
private CallNode getAnElementWriteCall() {
|
||||
result = this.getAMethodCall("[]=") and
|
||||
result.getNumberOfArguments() = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value stored as an element on this node, such as the `x` in `obj[key] = x`.
|
||||
*
|
||||
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver.
|
||||
*/
|
||||
Node getAnElementWriteValue() { result = this.getAnElementWriteCall().getArgument(1) }
|
||||
|
||||
/**
|
||||
* Gets the `x` in `obj[key] = x`, where this node flows to `obj`.
|
||||
*
|
||||
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver
|
||||
* and the first argument has constant value `key`.
|
||||
*/
|
||||
Node getAnElementWriteValue(ConstantValue key) {
|
||||
exists(CallNode call |
|
||||
call = this.getAnElementWriteCall() and
|
||||
call.getArgument(0).getConstantValue() = key and
|
||||
result = call.getArgument(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,18 +316,74 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
|
||||
}
|
||||
|
||||
cached
|
||||
private predicate hasLocalSource(Node sink, Node source) {
|
||||
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
|
||||
// recursive case, so instead we check it explicitly here.
|
||||
source = sink and
|
||||
source instanceof LocalSourceNode
|
||||
or
|
||||
exists(Node mid |
|
||||
hasLocalSource(mid, source) and
|
||||
localFlowStepTypeTracker(mid, sink)
|
||||
)
|
||||
private module Cached {
|
||||
cached
|
||||
predicate hasLocalSource(Node sink, Node source) {
|
||||
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the
|
||||
// recursive case, so instead we check it explicitly here.
|
||||
source = sink and
|
||||
source instanceof LocalSourceNode
|
||||
or
|
||||
exists(Node mid | hasLocalSource(mid, source) |
|
||||
localFlowStepTypeTracker(mid, sink)
|
||||
or
|
||||
// Explicitly include the SSA param input step as type-tracking omits this step.
|
||||
LocalFlow::localFlowSsaParamInput(mid, sink)
|
||||
or
|
||||
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
|
||||
)
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
|
||||
source.flowsTo(call.getReceiver()) and
|
||||
call.getMethodName() = name
|
||||
}
|
||||
|
||||
cached
|
||||
predicate hasYieldCall(BlockParameterNode block, CallNode yield) {
|
||||
exists(MethodBase method, YieldCall call |
|
||||
block.getMethod() = method and
|
||||
call.getEnclosingMethod() = method and
|
||||
yield.asExpr().getExpr() = call
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A place in which a named constant can be looked up during constant lookup.
|
||||
*/
|
||||
cached
|
||||
newtype TConstLookupScope =
|
||||
/** Look up in a qualified constant name `base::`. */
|
||||
MkQualifiedLookup(ConstantAccess base) or
|
||||
/** Look up in the ancestors of `mod`. */
|
||||
MkAncestorLookup(Module mod) or
|
||||
/** Look up in a module syntactically nested in a declaration of `mod`. */
|
||||
MkNestedLookup(Module mod) or
|
||||
/** Pseudo-scope for accesses that are known to resolve to `mod`. */
|
||||
MkExactLookup(Module mod)
|
||||
|
||||
/**
|
||||
* Gets a `LocalSourceNode` to represent the constant read or written by `access`.
|
||||
*/
|
||||
cached
|
||||
LocalSourceNode getConstantAccessNode(ConstantAccess access) {
|
||||
// Namespaces don't evaluate to the constant being accessed, they return the value of their last statement.
|
||||
// Use the definition of 'self' in the namespace as the representative in this case.
|
||||
if access instanceof Namespace
|
||||
then result = getNamespaceSelf(access)
|
||||
else result.asExpr().getExpr() = access
|
||||
}
|
||||
|
||||
cached
|
||||
predicate forceCachingInSameStage() { any() }
|
||||
|
||||
cached
|
||||
predicate forceCachingBackref() { exists(any(ConstRef const).getConst(_)) }
|
||||
}
|
||||
|
||||
private import Cached
|
||||
|
||||
/** Gets a node corresponding to expression `e`. */
|
||||
ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e }
|
||||
|
||||
@@ -559,3 +738,591 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode {
|
||||
result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a run-time module or class.
|
||||
*
|
||||
* This is equivalent to the type `Ast::Module` but provides data-flow specific methods.
|
||||
*/
|
||||
class ModuleNode instanceof Module {
|
||||
/** Gets a declaration of this module, if any. */
|
||||
final ModuleBase getADeclaration() { result = super.getADeclaration() }
|
||||
|
||||
/** Gets the super class of this module, if any. */
|
||||
final ModuleNode getSuperClass() { result = super.getSuperClass() }
|
||||
|
||||
/** Gets an immediate sub class of this module, if any. */
|
||||
final ModuleNode getASubClass() { result = super.getASubClass() }
|
||||
|
||||
/** Gets a `prepend`ed module. */
|
||||
final ModuleNode getAPrependedModule() { result = super.getAPrependedModule() }
|
||||
|
||||
/** Gets an `include`d module. */
|
||||
final ModuleNode getAnIncludedModule() { result = super.getAnIncludedModule() }
|
||||
|
||||
/** Gets the super class or an included or prepended module. */
|
||||
final ModuleNode getAnImmediateAncestor() { result = super.getAnImmediateAncestor() }
|
||||
|
||||
/** Gets a direct subclass or module including or prepending this one. */
|
||||
final ModuleNode getAnImmediateDescendent() { result = super.getAnImmediateDescendent() }
|
||||
|
||||
/** Gets a module that is transitively subclassed, included, or prepended by this module. */
|
||||
pragma[inline]
|
||||
final ModuleNode getAnAncestor() { result = super.getAnAncestor() }
|
||||
|
||||
/** Gets a module that transitively subclasses, includes, or prepends this module. */
|
||||
pragma[inline]
|
||||
final ModuleNode getADescendent() { result = super.getADescendent() }
|
||||
|
||||
/**
|
||||
* Gets the expression node denoting the super class or an included or prepended module.
|
||||
*
|
||||
* For example, `C` is an ancestor expression of `M` in each of the following examples:
|
||||
* ```rb
|
||||
* class M < C
|
||||
* end
|
||||
*
|
||||
* module M
|
||||
* include C
|
||||
* prepend C
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
final ExprNode getAnAncestorExpr() {
|
||||
result.asExpr().getExpr() = super.getADeclaration().getAnAncestorExpr()
|
||||
}
|
||||
|
||||
/** Holds if this module is a class. */
|
||||
predicate isClass() { super.isClass() }
|
||||
|
||||
/** Gets a textual representation of this module. */
|
||||
final string toString() { result = super.toString() }
|
||||
|
||||
/**
|
||||
* Gets the qualified name of this module, if any.
|
||||
*
|
||||
* Only modules that can be resolved will have a qualified name.
|
||||
*/
|
||||
final string getQualifiedName() { result = super.getQualifiedName() }
|
||||
|
||||
/** Gets the location of this module. */
|
||||
final Location getLocation() { result = super.getLocation() }
|
||||
|
||||
/**
|
||||
* Gets `self` in a declaration of this module.
|
||||
*
|
||||
* This only gets `self` at the module level, not inside any (singleton) method.
|
||||
*/
|
||||
LocalSourceNode getModuleLevelSelf() {
|
||||
result.(SsaDefinitionNode).getVariable() = super.getADeclaration().getModuleSelfVariable()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets `self` in the module declaration or in one of its singleton methods.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
LocalSourceNode getAnOwnModuleSelf() {
|
||||
result = this.getModuleLevelSelf()
|
||||
or
|
||||
result = this.getAnOwnSingletonMethod().getSelfParameter()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a call to method `name` on `self` in the module-level scope of this module.
|
||||
*
|
||||
* For example,
|
||||
* ```rb
|
||||
* module M
|
||||
* include A # getAModuleLevelCall("include")
|
||||
* foo :bar # getAModuleLevelCall("foo")
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
CallNode getAModuleLevelCall(string name) {
|
||||
result = this.getModuleLevelSelf().getAMethodCall(name)
|
||||
}
|
||||
|
||||
/** Gets a constant or `self` variable that refers to this module. */
|
||||
LocalSourceNode getAnImmediateReference() {
|
||||
result.asExpr().getExpr() = super.getAnImmediateReference()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a singleton method declared in this module (or in a singleton class
|
||||
* augmenting this module).
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
MethodNode getAnOwnSingletonMethod() {
|
||||
result.asCallableAstNode() = super.getAnOwnSingletonMethod()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the singleton method named `name` declared in this module (or in a singleton class
|
||||
* augmenting this module).
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
MethodNode getOwnSingletonMethod(string name) {
|
||||
result = this.getAnOwnSingletonMethod() and
|
||||
result.getMethodName() = name
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance method declared in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
MethodNode getAnOwnInstanceMethod() {
|
||||
result.asCallableAstNode() = this.getADeclaration().getAMethod().(Method)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance method named `name` declared in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
MethodNode getOwnInstanceMethod(string name) {
|
||||
result = this.getAnOwnInstanceMethod() and
|
||||
result.getMethodName() = name
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `self` parameter of an instance method declared in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
ParameterNode getAnOwnInstanceSelf() {
|
||||
result = TSelfParameterNode(this.getAnOwnInstanceMethod().asCallableAstNode())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `self` parameter of an instance method available in this module,
|
||||
* including those inherited from ancestors.
|
||||
*/
|
||||
ParameterNode getAnInstanceSelf() {
|
||||
// Make sure to include the 'self' in overridden instance methods
|
||||
result = this.getAnAncestor().getAnOwnInstanceSelf()
|
||||
}
|
||||
|
||||
private InstanceVariableAccess getAnOwnInstanceVariableAccess(string name) {
|
||||
exists(InstanceVariable v |
|
||||
v.getDeclaringScope() = this.getADeclaration() and
|
||||
v.getName() = name and
|
||||
result.getVariable() = v
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an access to the instance variable `name` in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
LocalSourceNode getAnOwnInstanceVariableRead(string name) {
|
||||
result.asExpr().getExpr() =
|
||||
this.getAnOwnInstanceVariableAccess(name).(InstanceVariableReadAccess)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the right-hand side of an assignment to the instance variable `name` in this module.
|
||||
*
|
||||
* Does not take inheritance into account.
|
||||
*/
|
||||
Node getAnOwnInstanceVariableWriteValue(string name) {
|
||||
exists(AssignExpr assignment |
|
||||
assignment.getLeftOperand() = this.getAnOwnInstanceVariableAccess(name) and
|
||||
result.asExpr().getExpr() = assignment.getRightOperand()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the instance method named `name` available in this module, including methods inherited
|
||||
* from ancestors.
|
||||
*
|
||||
* Overridden methods are not included.
|
||||
*/
|
||||
MethodNode getInstanceMethod(string name) {
|
||||
result.asCallableAstNode() = super.getInstanceMethod(name)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance method available in this module, including methods inherited
|
||||
* from ancestors.
|
||||
*
|
||||
* Overridden methods are not included.
|
||||
*/
|
||||
MethodNode getAnInstanceMethod() { result = this.getInstanceMethod(_) }
|
||||
|
||||
/**
|
||||
* Gets the enclosing module, as it appears in the qualified name of this module.
|
||||
*
|
||||
* For example, the canonical enclosing module of `A::B` is `A`, and `A` itself has no canonical enclosing module.
|
||||
*/
|
||||
ModuleNode getCanonicalEnclosingModule() { result = super.getParentModule() }
|
||||
|
||||
/**
|
||||
* Gets a module named `name` declared inside this one (not aliased), provided
|
||||
* that such a module is defined or reopened in the current codebase.
|
||||
*
|
||||
* For example, for `A::B` the canonical nested module named `C` would be `A::B::C`.
|
||||
*
|
||||
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
|
||||
* module whose qualified name is not `A::B::C`, then it will not be found by
|
||||
* this predicate.
|
||||
*/
|
||||
ModuleNode getCanonicalNestedModule(string name) { result = super.getNestedModule(name) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a run-time class.
|
||||
*/
|
||||
class ClassNode extends ModuleNode {
|
||||
ClassNode() { this.isClass() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to a method, block, or lambda expression.
|
||||
*/
|
||||
class CallableNode extends ExprNode {
|
||||
private Callable callable;
|
||||
|
||||
CallableNode() { this.asExpr().getExpr() = callable }
|
||||
|
||||
/** Gets the underlying AST node as a `Callable`. */
|
||||
Callable asCallableAstNode() { result = callable }
|
||||
|
||||
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
|
||||
node.isSourceParameterOf(callable, result)
|
||||
}
|
||||
|
||||
/** Gets the `n`th positional parameter. */
|
||||
ParameterNode getParameter(int n) { this.getParameterPosition(result).isPositional(n) }
|
||||
|
||||
/** Gets the keyword parameter of the given name. */
|
||||
ParameterNode getKeywordParameter(string name) {
|
||||
this.getParameterPosition(result).isKeyword(name)
|
||||
}
|
||||
|
||||
/** Gets the `self` parameter of this callable, if any. */
|
||||
ParameterNode getSelfParameter() { this.getParameterPosition(result).isSelf() }
|
||||
|
||||
/**
|
||||
* Gets the `hash-splat` parameter. This is a synthetic parameter holding
|
||||
* a hash object with entries for each keyword argument passed to the function.
|
||||
*/
|
||||
ParameterNode getHashSplatParameter() { this.getParameterPosition(result).isHashSplat() }
|
||||
|
||||
/**
|
||||
* Gets the block parameter of this method, if any.
|
||||
*/
|
||||
ParameterNode getBlockParameter() { this.getParameterPosition(result).isBlock() }
|
||||
|
||||
/**
|
||||
* Gets a `yield` in this method or `.call` on the block parameter.
|
||||
*/
|
||||
CallNode getABlockCall() {
|
||||
hasYieldCall(this.getBlockParameter(), result)
|
||||
or
|
||||
result = this.getBlockParameter().getAMethodCall("call")
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the canonical return node from this callable.
|
||||
*
|
||||
* Each callable has exactly one such node, and its location may not correspond
|
||||
* to any particular return site - consider using `getAReturningNode` to get nodes
|
||||
* whose locations correspond to return sites.
|
||||
*/
|
||||
Node getReturn() { result.(SynthReturnNode).getCfgScope() = callable }
|
||||
|
||||
/**
|
||||
* Gets a data flow node whose value is about to be returned by this callable.
|
||||
*/
|
||||
Node getAReturningNode() { result = this.getReturn().(SynthReturnNode).getAnInput() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to a method (possibly a singleton method).
|
||||
*/
|
||||
class MethodNode extends CallableNode {
|
||||
MethodNode() { super.asCallableAstNode() instanceof MethodBase }
|
||||
|
||||
/** Gets the underlying AST node for this method. */
|
||||
override MethodBase asCallableAstNode() { result = super.asCallableAstNode() }
|
||||
|
||||
/** Gets the name of this method. */
|
||||
string getMethodName() { result = this.asCallableAstNode().getName() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data flow node corresponding to a block argument.
|
||||
*/
|
||||
class BlockNode extends CallableNode {
|
||||
BlockNode() { super.asCallableAstNode() instanceof Block }
|
||||
|
||||
/** Gets the underlying AST node for this block. */
|
||||
override Block asCallableAstNode() { result = super.asCallableAstNode() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A representation of a pair such as `K => V` or `K: V`.
|
||||
*
|
||||
* Unlike most expressions, pairs do not evaluate to actual objects at runtime and their nodes
|
||||
* cannot generally be expected to have meaningful data flow edges.
|
||||
* This node simply provides convenient access to the key and value as data flow nodes.
|
||||
*/
|
||||
class PairNode extends ExprNode {
|
||||
PairNode() { this.getExprNode() instanceof CfgNodes::ExprNodes::PairCfgNode }
|
||||
|
||||
/**
|
||||
* Holds if this pair is of form `key => value` or `key: value`.
|
||||
*/
|
||||
predicate hasKeyAndValue(Node key, Node value) {
|
||||
exists(CfgNodes::ExprNodes::PairCfgNode n |
|
||||
this.getExprNode() = n and
|
||||
key = TExprNode(n.getKey()) and
|
||||
value = TExprNode(n.getValue())
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the key expression of this pair, such as the `K` in `K => V` or `K: V`. */
|
||||
Node getKey() { this.hasKeyAndValue(result, _) }
|
||||
|
||||
/** Gets the value expression of this pair, such as the `V` in `K => V` or `K: V`. */
|
||||
Node getValue() { this.hasKeyAndValue(_, result) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node that corresponds to a hash literal. Hash literals are desugared
|
||||
* into calls to `Hash.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class HashLiteralNode extends LocalSourceNode, ExprNode {
|
||||
HashLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode }
|
||||
|
||||
/** Gets a pair in this hash literal. */
|
||||
PairNode getAKeyValuePair() {
|
||||
result.getExprNode() =
|
||||
super.getExprNode().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()
|
||||
}
|
||||
|
||||
/** Gets the value associated with the constant `key`, if known. */
|
||||
Node getElementFromKey(ConstantValue key) {
|
||||
exists(ExprNode keyNode |
|
||||
this.getAKeyValuePair().hasKeyAndValue(keyNode, result) and
|
||||
keyNode.getConstantValue() = key
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A data-flow node corresponding to an array literal. Array literals are desugared
|
||||
* into calls to `Array.[]`, so this includes both desugared calls as well as
|
||||
* explicit calls.
|
||||
*/
|
||||
class ArrayLiteralNode extends LocalSourceNode, ExprNode {
|
||||
ArrayLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode }
|
||||
|
||||
/**
|
||||
* Gets an element of the array.
|
||||
*/
|
||||
Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) }
|
||||
}
|
||||
|
||||
/**
|
||||
* An access to a constant, such as `C`, `C::D`, or a class or module declaration.
|
||||
*
|
||||
* See `DataFlow::getConst` for usage example.
|
||||
*/
|
||||
class ConstRef extends LocalSourceNode {
|
||||
private ConstantAccess access;
|
||||
|
||||
ConstRef() { this = getConstantAccessNode(access) }
|
||||
|
||||
/** Gets the underlying constant access AST node. */
|
||||
ConstantAccess asConstantAccess() { result = access }
|
||||
|
||||
/** Gets the underlying module declaration, if any. */
|
||||
Namespace asNamespaceDeclaration() { result = access }
|
||||
|
||||
/** Gets the module defined or re-opened by this constant access, if any. */
|
||||
ModuleNode asModule() { result.getADeclaration() = access }
|
||||
|
||||
/**
|
||||
* Gets the simple name of the constant being referenced, such as
|
||||
* the `B` in `A::B`.
|
||||
*/
|
||||
string getName() { result = access.getName() }
|
||||
|
||||
/**
|
||||
* Holds if this might refer to a top-level constant.
|
||||
*/
|
||||
predicate isPossiblyGlobal() {
|
||||
exists(Module mod |
|
||||
not exists(mod.getParentModule()) and
|
||||
mod.getAnImmediateReference() = access
|
||||
)
|
||||
or
|
||||
not exists(Module mod | mod.getAnImmediateReference() = access) and
|
||||
not exists(access.getScopeExpr())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a module for which this constant is the reference to an ancestor module.
|
||||
*
|
||||
* For example, `M` is the ancestry target of `C` in the following examples:
|
||||
* ```rb
|
||||
* class M < C {}
|
||||
*
|
||||
* module M
|
||||
* include C
|
||||
* end
|
||||
*
|
||||
* module M
|
||||
* prepend C
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this }
|
||||
|
||||
/**
|
||||
* Gets the known target module.
|
||||
*
|
||||
* We resolve these differently to prune out infeasible constant lookups.
|
||||
*/
|
||||
private Module getExactTarget() { result.getAnImmediateReference() = access }
|
||||
|
||||
/**
|
||||
* Gets a scope in which a constant lookup may access the contents of the module referenced by this constant.
|
||||
*/
|
||||
cached
|
||||
private TConstLookupScope getATargetScope() {
|
||||
forceCachingInSameStage() and
|
||||
result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*())
|
||||
or
|
||||
access = any(ConstantAccess ac).getScopeExpr() and
|
||||
result = MkQualifiedLookup(access)
|
||||
or
|
||||
result = MkNestedLookup(this.getAncestryTarget())
|
||||
or
|
||||
result = MkExactLookup(access.(Namespace).getModule())
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes).
|
||||
*
|
||||
* Top-levels are not included, since this is only needed for nested constant lookup, and unqualified constants
|
||||
* at the top-level are handled by `DataFlow::getConst`, never `ConstRef.getConst`.
|
||||
*/
|
||||
private TConstLookupScope getLookupScope() {
|
||||
result = MkQualifiedLookup(access.getScopeExpr())
|
||||
or
|
||||
not exists(this.getExactTarget()) and
|
||||
not exists(access.getScopeExpr()) and
|
||||
not access.hasGlobalScope() and
|
||||
(
|
||||
result = MkAncestorLookup(access.getEnclosingModule().getNamespaceOrToplevel().getModule())
|
||||
or
|
||||
result = MkNestedLookup(access.getEnclosingModule().getEnclosingModule*().getModule())
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this can reference a constant named `name` from `scope` using a lookup of `kind`.
|
||||
*/
|
||||
cached
|
||||
private predicate accesses(TConstLookupScope scope, string name) {
|
||||
forceCachingInSameStage() and
|
||||
scope = this.getLookupScope() and
|
||||
name = this.getName()
|
||||
or
|
||||
exists(Module mod |
|
||||
this.getExactTarget() = mod.getNestedModule(name) and
|
||||
scope = MkExactLookup(mod)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a constant reference that may resolve to a member of this node.
|
||||
*
|
||||
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
|
||||
* ```rb
|
||||
* A::B # simple reference
|
||||
*
|
||||
* module A
|
||||
* B # in scope
|
||||
* module X
|
||||
* B # in nested scope
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* module X
|
||||
* include A
|
||||
* B # via inclusion
|
||||
* end
|
||||
*
|
||||
* class X < A
|
||||
* B # via subclassing
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
pragma[inline]
|
||||
ConstRef getConst(string name) {
|
||||
exists(TConstLookupScope scope |
|
||||
pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and
|
||||
result.accesses(pragma[only_bind_out](scope), name)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a module that transitively subclasses, includes, or prepends the module referred to by
|
||||
* this constant.
|
||||
*
|
||||
* For example, `DataFlow::getConst("A").getADescendentModule()` finds `B`, `C`, and `E`:
|
||||
* ```rb
|
||||
* class B < A
|
||||
* end
|
||||
*
|
||||
* class C < B
|
||||
* end
|
||||
*
|
||||
* module E
|
||||
* include C
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a constant reference that may resolve to the top-level constant `name`.
|
||||
*
|
||||
* To get nested constants, call `getConst()` one or more times on the result.
|
||||
*
|
||||
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
|
||||
* ```rb
|
||||
* A::B # simple reference
|
||||
*
|
||||
* module A
|
||||
* B # in scope
|
||||
* module X
|
||||
* B # in nested scope
|
||||
* end
|
||||
* end
|
||||
*
|
||||
* module X
|
||||
* include A
|
||||
* B # via inclusion
|
||||
* end
|
||||
*
|
||||
* class X < A
|
||||
* B # via subclassing
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
pragma[nomagic]
|
||||
ConstRef getConst(string name) {
|
||||
result.getName() = name and
|
||||
result.isPossiblyGlobal()
|
||||
}
|
||||
|
||||
@@ -115,8 +115,8 @@ private module Cached {
|
||||
*/
|
||||
cached
|
||||
predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
|
||||
defaultAdditionalTaintStep(nodeFrom, nodeTo)
|
||||
or
|
||||
DataFlow::localFlowStep(nodeFrom, nodeTo) or
|
||||
defaultAdditionalTaintStep(nodeFrom, nodeTo) or
|
||||
// Simple flow through library code is included in the exposed local
|
||||
// step relation, even though flow is technically inter-procedural
|
||||
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)
|
||||
|
||||
@@ -24,7 +24,7 @@ deprecated class ParamsCall = Rails::ParamsCall;
|
||||
deprecated class CookiesCall = Rails::CookiesCall;
|
||||
|
||||
/**
|
||||
* A `ClassDeclaration` for a class that extends `ActionController::Base`.
|
||||
* A class that extends `ActionController::Base`.
|
||||
* For example,
|
||||
*
|
||||
* ```rb
|
||||
@@ -36,26 +36,61 @@ deprecated class CookiesCall = Rails::CookiesCall;
|
||||
* end
|
||||
* ```
|
||||
*/
|
||||
class ActionControllerControllerClass extends ClassDeclaration {
|
||||
ActionControllerControllerClass() {
|
||||
this.getSuperclassExpr() =
|
||||
class ActionControllerClass extends DataFlow::ClassNode {
|
||||
ActionControllerClass() {
|
||||
this =
|
||||
[
|
||||
API::getTopLevelMember("ActionController").getMember("Base"),
|
||||
DataFlow::getConst("ActionController").getConst("Base"),
|
||||
// In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
|
||||
// treat it separately in case the `ApplicationController` definition is not in the database.
|
||||
API::getTopLevelMember("ApplicationController"),
|
||||
DataFlow::getConst("ApplicationController"),
|
||||
// ActionController::Metal technically doesn't contain all of the
|
||||
// methods available in Base, such as those for rendering views.
|
||||
// However we prefer to be over-sensitive in this case in order to find
|
||||
// more results.
|
||||
API::getTopLevelMember("ActionController").getMember("Metal")
|
||||
].getASubclass().getAValueReachableFromSource().asExpr().getExpr()
|
||||
DataFlow::getConst("ActionController").getConst("Metal")
|
||||
].getADescendentModule()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `ActionControllerActionMethod` defined in this class.
|
||||
*/
|
||||
ActionControllerActionMethod getAnAction() {
|
||||
result = this.getAnInstanceMethod().asCallableAstNode()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `self` that possibly refers to an instance of this class.
|
||||
*/
|
||||
DataFlow::LocalSourceNode getSelf() {
|
||||
result = this.getAnInstanceSelf()
|
||||
or
|
||||
// Include the module-level `self` to recover some cases where a block at the module level
|
||||
// is invoked with an instance as the `self`, which we currently can't model directly.
|
||||
// Concretely this happens in the block passed to `rescue_from`.
|
||||
// TODO: revisit when we have better infrastructure for handling self in a block
|
||||
result = this.getModuleLevelSelf()
|
||||
}
|
||||
}
|
||||
|
||||
private DataFlow::LocalSourceNode actionControllerInstance() {
|
||||
result = any(ActionControllerClass cls).getSelf()
|
||||
}
|
||||
|
||||
/**
|
||||
* DEPRECATED. Use `ActionControllerClass` instead.
|
||||
*
|
||||
* A `ClassDeclaration` corresponding to an `ActionControllerClass`.
|
||||
*/
|
||||
deprecated class ActionControllerControllerClass extends ClassDeclaration {
|
||||
ActionControllerControllerClass() { this = any(ActionControllerClass cls).getADeclaration() }
|
||||
|
||||
/**
|
||||
* Gets a `ActionControllerActionMethod` defined in this class.
|
||||
*/
|
||||
ActionControllerActionMethod getAnAction() { result = this.getAMethod() }
|
||||
ActionControllerActionMethod getAnAction() {
|
||||
result = this.getAMethod().(Method) and result.isPrivate()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,9 +98,11 @@ class ActionControllerControllerClass extends ClassDeclaration {
|
||||
* This may be the target of a route handler, if such a route is defined.
|
||||
*/
|
||||
class ActionControllerActionMethod extends Method, Http::Server::RequestHandler::Range {
|
||||
private ActionControllerControllerClass controllerClass;
|
||||
private ActionControllerClass controllerClass;
|
||||
|
||||
ActionControllerActionMethod() { this = controllerClass.getAMethod() and not this.isPrivate() }
|
||||
ActionControllerActionMethod() {
|
||||
this = controllerClass.getAnInstanceMethod().asCallableAstNode() and not this.isPrivate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Establishes a mapping between a method within the file
|
||||
@@ -91,7 +128,7 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
|
||||
/**
|
||||
* Gets the controller class containing this method.
|
||||
*/
|
||||
ActionControllerControllerClass getControllerClass() {
|
||||
ActionControllerClass getControllerClass() {
|
||||
// TODO: model the implicit render call when a path through the method does
|
||||
// not end at an explicit render or redirect
|
||||
result = controllerClass
|
||||
@@ -102,37 +139,23 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
|
||||
* May return multiple results.
|
||||
*/
|
||||
ActionDispatch::Routing::Route getARoute() {
|
||||
exists(string name |
|
||||
exists(string name, DataFlow::MethodNode m |
|
||||
isRoute(result, name, controllerClass) and
|
||||
isActionControllerMethod(this, name, controllerClass)
|
||||
m = controllerClass.getInstanceMethod(name) and
|
||||
this = m.asCallableAstNode()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isRoute(
|
||||
ActionDispatch::Routing::Route route, string name, ActionControllerControllerClass controllerClass
|
||||
ActionDispatch::Routing::Route route, string name, ActionControllerClass controllerClass
|
||||
) {
|
||||
route.getController() + "_controller" =
|
||||
ActionDispatch::Routing::underscore(controllerClass.getAQualifiedName()) and
|
||||
ActionDispatch::Routing::underscore(controllerClass.getQualifiedName()) and
|
||||
name = route.getAction()
|
||||
}
|
||||
|
||||
// A method call with a `self` receiver from within a controller class
|
||||
private class ActionControllerContextCall extends MethodCall {
|
||||
private ActionControllerControllerClass controllerClass;
|
||||
|
||||
ActionControllerContextCall() {
|
||||
this.getReceiver() instanceof SelfVariableAccess and
|
||||
this.getEnclosingModule() = controllerClass
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the controller class containing this method.
|
||||
*/
|
||||
ActionControllerControllerClass getControllerClass() { result = controllerClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `RemoteFlowSource::Range` to represent accessing the
|
||||
* ActionController parameters available via the `params` method.
|
||||
@@ -158,13 +181,17 @@ class CookiesSource extends Http::Server::RequestInputAccess::Range {
|
||||
}
|
||||
|
||||
/** A call to `cookies` from within a controller. */
|
||||
private class ActionControllerCookiesCall extends ActionControllerContextCall, CookiesCallImpl {
|
||||
ActionControllerCookiesCall() { this.getMethodName() = "cookies" }
|
||||
private class ActionControllerCookiesCall extends CookiesCallImpl {
|
||||
ActionControllerCookiesCall() {
|
||||
this = actionControllerInstance().getAMethodCall("cookies").asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `params` from within a controller. */
|
||||
private class ActionControllerParamsCall extends ActionControllerContextCall, ParamsCallImpl {
|
||||
ActionControllerParamsCall() { this.getMethodName() = "params" }
|
||||
private class ActionControllerParamsCall extends ParamsCallImpl {
|
||||
ActionControllerParamsCall() {
|
||||
this = actionControllerInstance().getAMethodCall("params").asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** Modeling for `ActionDispatch::Request`. */
|
||||
@@ -174,10 +201,7 @@ private module Request {
|
||||
* `ActionDispatch::Request`.
|
||||
*/
|
||||
private class RequestNode extends DataFlow::CallNode {
|
||||
RequestNode() {
|
||||
this.asExpr().getExpr() instanceof ActionControllerContextCall and
|
||||
this.getMethodName() = "request"
|
||||
}
|
||||
RequestNode() { this = actionControllerInstance().getAMethodCall("request") }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -234,7 +258,7 @@ private module Request {
|
||||
// Request headers are prefixed with `HTTP_` to distinguish them from
|
||||
// "headers" supplied by Rack middleware.
|
||||
this.getMethodName() = ["get_header", "fetch_header"] and
|
||||
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
|
||||
this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
|
||||
}
|
||||
|
||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
|
||||
@@ -290,9 +314,8 @@ private module Request {
|
||||
*/
|
||||
private class EnvHttpAccess extends DataFlow::CallNode, Http::Server::RequestInputAccess::Range {
|
||||
EnvHttpAccess() {
|
||||
any(EnvCall c).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver()) and
|
||||
this.getMethodName() = "[]" and
|
||||
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
|
||||
this = any(EnvCall c).getAMethodCall("[]") and
|
||||
this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
|
||||
}
|
||||
|
||||
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
|
||||
@@ -302,21 +325,32 @@ private module Request {
|
||||
}
|
||||
|
||||
/** A call to `render` from within a controller. */
|
||||
private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCallImpl {
|
||||
ActionControllerRenderCall() { this.getMethodName() = "render" }
|
||||
private class ActionControllerRenderCall extends RenderCallImpl {
|
||||
ActionControllerRenderCall() {
|
||||
this = actionControllerInstance().getAMethodCall("render").asExpr().getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `render_to` from within a controller. */
|
||||
private class ActionControllerRenderToCall extends ActionControllerContextCall, RenderToCallImpl {
|
||||
ActionControllerRenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] }
|
||||
private class ActionControllerRenderToCall extends RenderToCallImpl {
|
||||
ActionControllerRenderToCall() {
|
||||
this =
|
||||
actionControllerInstance()
|
||||
.getAMethodCall(["render_to_body", "render_to_string"])
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
/** A call to `html_escape` from within a controller. */
|
||||
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
|
||||
ActionControllerHtmlEscapeCall() {
|
||||
// "h" is aliased to "html_escape" in ActiveSupport
|
||||
this.getMethodName() = ["html_escape", "html_escape_once", "h", "sanitize"] and
|
||||
this.getEnclosingModule() instanceof ActionControllerControllerClass
|
||||
this =
|
||||
actionControllerInstance()
|
||||
.getAMethodCall(["html_escape", "html_escape_once", "h", "sanitize"])
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,9 +358,16 @@ private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
|
||||
* A call to the `redirect_to` method, used in an action to redirect to a
|
||||
* specific URL/path or to a different action in this controller.
|
||||
*/
|
||||
class RedirectToCall extends ActionControllerContextCall {
|
||||
class RedirectToCall extends MethodCall {
|
||||
private ActionControllerClass controller;
|
||||
|
||||
RedirectToCall() {
|
||||
this.getMethodName() = ["redirect_to", "redirect_back", "redirect_back_or_to"]
|
||||
this =
|
||||
controller
|
||||
.getSelf()
|
||||
.getAMethodCall(["redirect_to", "redirect_back", "redirect_back_or_to"])
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
}
|
||||
|
||||
/** Gets the `Expr` representing the URL to redirect to, if any */
|
||||
@@ -338,8 +379,10 @@ class RedirectToCall extends ActionControllerContextCall {
|
||||
|
||||
/** Gets the `ActionControllerActionMethod` to redirect to, if any */
|
||||
ActionControllerActionMethod getRedirectActionMethod() {
|
||||
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(result.getName()) and
|
||||
result.getEnclosingModule() = this.getControllerClass()
|
||||
exists(string name |
|
||||
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(name) and
|
||||
result = controller.getInstanceMethod(name).asCallableAstNode()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -373,18 +416,8 @@ class ActionControllerRedirectResponse extends Http::Server::HttpRedirectRespons
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate isActionControllerMethod(Method m, string name, ActionControllerControllerClass c) {
|
||||
m.getName() = name and
|
||||
m.getEnclosingModule() = c
|
||||
}
|
||||
|
||||
pragma[nomagic]
|
||||
private predicate actionControllerHasHelperMethodCall(ActionControllerControllerClass c, string name) {
|
||||
exists(MethodCall mc |
|
||||
mc.getMethodName() = "helper_method" and
|
||||
mc.getAnArgument().getConstantValue().isStringlikeValue(name) and
|
||||
mc.getEnclosingModule() = c
|
||||
)
|
||||
private predicate actionControllerHasHelperMethodCall(ActionControllerClass c, string name) {
|
||||
c.getAModuleLevelCall("helper_method").getArgument(_).getConstantValue().isStringlikeValue(name)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,27 +437,28 @@ private predicate actionControllerHasHelperMethodCall(ActionControllerController
|
||||
* See also https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method
|
||||
*/
|
||||
class ActionControllerHelperMethod extends Method {
|
||||
private ActionControllerControllerClass controllerClass;
|
||||
private ActionControllerClass controllerClass;
|
||||
|
||||
ActionControllerHelperMethod() {
|
||||
exists(string name |
|
||||
isActionControllerMethod(this, name, controllerClass) and
|
||||
actionControllerHasHelperMethodCall(controllerClass, name)
|
||||
exists(DataFlow::MethodNode m, string name |
|
||||
m = controllerClass.getInstanceMethod(name) and
|
||||
actionControllerHasHelperMethodCall(controllerClass, name) and
|
||||
this = m.asCallableAstNode()
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the class containing this helper method. */
|
||||
ActionControllerControllerClass getControllerClass() { result = controllerClass }
|
||||
ActionControllerClass getControllerClass() { result = controllerClass }
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an `ActionControllerControllerClass` associated with the given `ErbFile`
|
||||
* Gets an `ActionControllerClass` associated with the given `ErbFile`
|
||||
* according to Rails path conventions.
|
||||
* For instance, a template file at `app/views/foo/bar/baz.html.erb` will be
|
||||
* mapped to a controller class in `app/controllers/foo/bar/baz_controller.rb`,
|
||||
* if such a controller class exists.
|
||||
*/
|
||||
ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) {
|
||||
ActionControllerClass getAssociatedControllerClass(ErbFile f) {
|
||||
// There is a direct mapping from template file to controller class
|
||||
controllerTemplateFile(result, f)
|
||||
or
|
||||
@@ -442,13 +476,13 @@ ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) {
|
||||
// TODO: improve layout support, e.g. for `layout` method
|
||||
// https://guides.rubyonrails.org/layouts_and_rendering.html
|
||||
/**
|
||||
* Holds if `templatesFile` is a viable file "belonging" to the given
|
||||
* Holds if `templateFile` is a viable file "belonging" to the given
|
||||
* `ActionControllerControllerClass`, according to Rails conventions.
|
||||
*
|
||||
* This handles mappings between controllers in `app/controllers/`, and
|
||||
* templates in `app/views/` and `app/views/layouts/`.
|
||||
*/
|
||||
predicate controllerTemplateFile(ActionControllerControllerClass cls, ErbFile templateFile) {
|
||||
predicate controllerTemplateFile(ActionControllerClass cls, ErbFile templateFile) {
|
||||
exists(string templatesPath, string sourcePrefix, string subPath, string controllerPath |
|
||||
controllerPath = cls.getLocation().getFile().getRelativePath() and
|
||||
templatesPath = templateFile.getParentContainer().getRelativePath() and
|
||||
@@ -484,16 +518,14 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
|
||||
/**
|
||||
* A call to `protect_from_forgery`.
|
||||
*/
|
||||
private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range {
|
||||
private ActionControllerContextCall callExpr;
|
||||
|
||||
private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range,
|
||||
DataFlow::CallNode {
|
||||
ActionControllerProtectFromForgeryCall() {
|
||||
callExpr = this.asExpr().getExpr() and
|
||||
callExpr.getMethodName() = "protect_from_forgery"
|
||||
this = actionControllerInstance().getAMethodCall("protect_from_forgery")
|
||||
}
|
||||
|
||||
private string getWithValueText() {
|
||||
result = callExpr.getKeywordArgument("with").getConstantValue().getSymbol()
|
||||
result = this.getKeywordArgument("with").getConstantValue().getSymbol()
|
||||
}
|
||||
|
||||
// Calls without `with: :exception` can allow for bypassing CSRF protection
|
||||
@@ -508,11 +540,7 @@ private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetti
|
||||
*/
|
||||
private class SendFile extends FileSystemAccess::Range, DataFlow::CallNode {
|
||||
SendFile() {
|
||||
this.getMethodName() = "send_file" and
|
||||
(
|
||||
this.asExpr().getExpr() instanceof ActionControllerContextCall or
|
||||
this.getReceiver().asExpr().getExpr() instanceof Response::ResponseCall
|
||||
)
|
||||
this = [actionControllerInstance(), Response::response()].getAMethodCall("send_file")
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
|
||||
@@ -522,21 +550,13 @@ private module ParamsSummaries {
|
||||
private import codeql.ruby.dataflow.FlowSummary
|
||||
|
||||
/**
|
||||
* An instance of `ActionController::Parameters`, including those returned
|
||||
* Gets an instance of `ActionController::Parameters`, including those returned
|
||||
* from method calls on other instances.
|
||||
*/
|
||||
private class ParamsInstance extends DataFlow::Node {
|
||||
ParamsInstance() {
|
||||
this.asExpr().getExpr() instanceof Rails::ParamsCall
|
||||
or
|
||||
this =
|
||||
any(DataFlow::CallNode call |
|
||||
call.getReceiver() instanceof ParamsInstance and
|
||||
call.getMethodName() = paramsMethodReturningParamsInstance()
|
||||
)
|
||||
or
|
||||
exists(ParamsInstance prev | prev.(DataFlow::LocalSourceNode).flowsTo(this))
|
||||
}
|
||||
private DataFlow::LocalSourceNode paramsInstance() {
|
||||
result.asExpr().getExpr() instanceof Rails::ParamsCall
|
||||
or
|
||||
result = paramsInstance().getAMethodCall(paramsMethodReturningParamsInstance())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -578,8 +598,7 @@ private module ParamsSummaries {
|
||||
MethodsReturningParamsInstanceSummary() { this = "ActionController::Parameters#<various>" }
|
||||
|
||||
override MethodCall getACall() {
|
||||
any(ParamsInstance i).asExpr().getExpr() = result.getReceiver() and
|
||||
result.getMethodName() = methodReturnsTaintFromSelf()
|
||||
result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
@@ -601,9 +620,8 @@ private module ParamsSummaries {
|
||||
|
||||
override MethodCall getACall() {
|
||||
result.getMethodName() = ["merge", "reverse_merge", "with_defaults"] and
|
||||
exists(ParamsInstance i |
|
||||
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)]
|
||||
)
|
||||
paramsInstance().getALocalUse().asExpr().getExpr() =
|
||||
[result.getReceiver(), result.getArgument(0)]
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
@@ -625,9 +643,8 @@ private module ParamsSummaries {
|
||||
|
||||
override MethodCall getACall() {
|
||||
result.getMethodName() = ["merge!", "reverse_merge!", "with_defaults!"] and
|
||||
exists(ParamsInstance i |
|
||||
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)]
|
||||
)
|
||||
paramsInstance().getALocalUse().asExpr().getExpr() =
|
||||
[result.getReceiver(), result.getArgument(0)]
|
||||
}
|
||||
|
||||
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
|
||||
@@ -643,15 +660,12 @@ private module ParamsSummaries {
|
||||
* response.
|
||||
*/
|
||||
private module Response {
|
||||
class ResponseCall extends ActionControllerContextCall {
|
||||
ResponseCall() { this.getMethodName() = "response" }
|
||||
DataFlow::LocalSourceNode response() {
|
||||
result = actionControllerInstance().getAMethodCall("response")
|
||||
}
|
||||
|
||||
class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
|
||||
BodyWrite() {
|
||||
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
|
||||
this.getMethodName() = "body="
|
||||
}
|
||||
BodyWrite() { this = response().getAMethodCall("body=") }
|
||||
|
||||
override DataFlow::Node getBody() { result = this.getArgument(0) }
|
||||
|
||||
@@ -661,10 +675,7 @@ private module Response {
|
||||
}
|
||||
|
||||
class SendFileCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
|
||||
SendFileCall() {
|
||||
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
|
||||
this.getMethodName() = "send_file"
|
||||
}
|
||||
SendFileCall() { this = response().getAMethodCall("send_file") }
|
||||
|
||||
override DataFlow::Node getBody() { result = this.getArgument(0) }
|
||||
|
||||
@@ -677,19 +688,12 @@ private module Response {
|
||||
HeaderWrite() {
|
||||
// response.header[key] = val
|
||||
// response.headers[key] = val
|
||||
exists(MethodCall headerCall |
|
||||
headerCall.getMethodName() = ["header", "headers"] and
|
||||
headerCall.getReceiver() instanceof ResponseCall
|
||||
|
|
||||
this.getReceiver().asExpr().getExpr() = headerCall and
|
||||
this.getMethodName() = "[]="
|
||||
)
|
||||
this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=")
|
||||
or
|
||||
// response.set_header(key) = val
|
||||
// response[header] = val
|
||||
// response.add_header(key, val)
|
||||
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
|
||||
this.getMethodName() = ["set_header", "[]=", "add_header"]
|
||||
this = response().getAMethodCall(["set_header", "[]=", "add_header"])
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
@@ -702,12 +706,12 @@ private module Response {
|
||||
class SpecificHeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range {
|
||||
SpecificHeaderWrite() {
|
||||
// response.<method> = val
|
||||
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
|
||||
this.getMethodName() =
|
||||
[
|
||||
"location=", "cache_control=", "_cache_control=", "etag=", "charset=", "content_type=",
|
||||
"date=", "last_modified=", "weak_etag=", "strong_etag="
|
||||
]
|
||||
this =
|
||||
response()
|
||||
.getAMethodCall([
|
||||
"location=", "cache_control=", "_cache_control=", "etag=", "charset=",
|
||||
"content_type=", "date=", "last_modified=", "weak_etag=", "strong_etag="
|
||||
])
|
||||
}
|
||||
|
||||
override string getName() {
|
||||
|
||||
@@ -571,9 +571,7 @@ class ActiveRecordAssociation extends DataFlow::CallNode {
|
||||
* For example, in `has_many :posts`, this is `post`.
|
||||
*/
|
||||
string getTargetModelName() {
|
||||
exists(string s |
|
||||
s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue()
|
||||
|
|
||||
exists(string s | s = this.getArgument(0).getConstantValue().getStringlikeValue() |
|
||||
// has_one :profile
|
||||
// belongs_to :user
|
||||
this.isSingular() and
|
||||
|
||||
@@ -182,3 +182,39 @@ module FileUtils {
|
||||
override DataFlow::Node getAPermissionNode() { result = permissionArg }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes and predicates for modeling the core `Dir` module.
|
||||
*/
|
||||
module Dir {
|
||||
/**
|
||||
* A call to a method on `Dir` that operates on a path as its first argument, and produces file-names.
|
||||
* Considered as a `FileNameSource` and a `FileSystemAccess`.
|
||||
*/
|
||||
class DirGlob extends FileSystemAccess::Range, FileNameSource instanceof DataFlow::CallNode {
|
||||
DirGlob() {
|
||||
this =
|
||||
API::getTopLevelMember("Dir")
|
||||
.getAMethodCall(["glob", "[]", "children", "each_child", "entries", "foreach"])
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
|
||||
}
|
||||
|
||||
/**
|
||||
* A call to a method on `Dir` that operates on a path as its first argument, considered as a `FileSystemAccess`.
|
||||
*/
|
||||
class DirPathAccess extends FileSystemAccess::Range instanceof DataFlow::CallNode {
|
||||
DirPathAccess() {
|
||||
this =
|
||||
API::getTopLevelMember("Dir")
|
||||
.getAMethodCall([
|
||||
"chdir", "chroot", "delete", "empty?", "exist?", "exists?", "mkdir", "new", "open",
|
||||
"rmdir", "unlink"
|
||||
])
|
||||
}
|
||||
|
||||
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
|
||||
}
|
||||
// TODO: Model that `(Dir.new "foo").each { |f| ... }` yields a filename (and some other public methods)
|
||||
}
|
||||
|
||||
@@ -92,102 +92,63 @@ module Rails {
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
|
||||
* Gets a reference to the `Rails` constant.
|
||||
*/
|
||||
private DataFlow::ConstRef rails() { result = DataFlow::getConst("Rails") }
|
||||
|
||||
/**
|
||||
* Gets a reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
|
||||
* `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database.
|
||||
*/
|
||||
private class RailtieClassAccess extends ConstantReadAccess {
|
||||
RailtieClassAccess() {
|
||||
this.getScopeExpr().(ConstantAccess).getName() = "Rails" and
|
||||
this.getName() = ["Railtie", "Engine", "Application"]
|
||||
}
|
||||
private DataFlow::ConstRef railtie() {
|
||||
result = rails().getConst(["Railtie", "Engine", "Application"])
|
||||
}
|
||||
|
||||
// A `ClassDeclaration` that (transitively) extends `Rails::Railtie`
|
||||
private class RailtieClass extends ClassDeclaration {
|
||||
RailtieClass() {
|
||||
this.getSuperclassExpr() instanceof RailtieClassAccess or
|
||||
exists(RailtieClass other |
|
||||
other.getModule() = this.getSuperclassExpr().(ConstantReadAccess).getModule()
|
||||
)
|
||||
}
|
||||
}
|
||||
/** Gets a class that transitively extends `Rails::Railtie` */
|
||||
private DataFlow::ClassNode railtieClass() { result = railtie().getADescendentModule() }
|
||||
|
||||
private DataFlow::CallNode getAConfigureCallNode() {
|
||||
// `Rails.application.configure`
|
||||
result = API::getTopLevelMember("Rails").getReturn("application").getAMethodCall("configure")
|
||||
/**
|
||||
* Gets a reference to `Rails::Application` or `Rails.application`.
|
||||
*/
|
||||
private DataFlow::LocalSourceNode railsApp() {
|
||||
result = rails().getAMethodCall("application")
|
||||
or
|
||||
// `Rails::Application.configure`
|
||||
exists(ConstantReadAccess read, RailtieClass cls |
|
||||
read = result.getReceiver().asExpr().getExpr() and
|
||||
read.getModule() = cls.getModule() and
|
||||
result.asExpr().getExpr().(MethodCall).getMethodName() = "configure"
|
||||
)
|
||||
result = rails().getConst("Application")
|
||||
}
|
||||
|
||||
/**
|
||||
* Classes representing accesses to the Rails config object.
|
||||
*/
|
||||
private module Config {
|
||||
/**
|
||||
* An access to a Rails config object.
|
||||
*/
|
||||
private class SourceNode extends DataFlow::LocalSourceNode {
|
||||
SourceNode() {
|
||||
// `Foo < Rails::Application ... config ...`
|
||||
exists(MethodCall configCall | this.asExpr().getExpr() = configCall |
|
||||
configCall.getMethodName() = "config" and
|
||||
configCall.getEnclosingModule() instanceof RailtieClass
|
||||
)
|
||||
or
|
||||
// `Rails.application.config`
|
||||
this = API::getTopLevelMember("Rails").getReturn("application").getReturn("config").asSource()
|
||||
or
|
||||
// `Rails.application.configure { ... config ... }`
|
||||
// `Rails::Application.configure { ... config ... }`
|
||||
exists(DataFlow::CallNode configureCallNode, Block block, MethodCall configCall |
|
||||
configCall = this.asExpr().getExpr()
|
||||
|
|
||||
configureCallNode = getAConfigureCallNode() and
|
||||
block = configureCallNode.asExpr().getExpr().(MethodCall).getBlock() and
|
||||
configCall.getParent+() = block and
|
||||
configCall.getMethodName() = "config"
|
||||
)
|
||||
}
|
||||
DataFlow::LocalSourceNode configSource() {
|
||||
// `Foo < Rails::Application ... config ...`
|
||||
result = railtieClass().getAnOwnModuleSelf().getAMethodCall("config")
|
||||
or
|
||||
// `Rails.application.config`
|
||||
result = railsApp().getAMethodCall("config")
|
||||
or
|
||||
// TODO: move away from getParent+() when have better infrastructure for overridden 'self' in blocks
|
||||
// `Rails.application.configure { ... config ... }`
|
||||
// `Rails::Application.configure { ... config ... }`
|
||||
exists(Block block, MethodCall configCall | configCall = result.asExpr().getExpr() |
|
||||
block = railsApp().getAMethodCall("configure").getBlock().asExpr().getExpr() and
|
||||
configCall.getParent+() = block and
|
||||
configCall.getMethodName() = "config"
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to the Rails config object.
|
||||
* Gets a reference to the ActionController config object.
|
||||
*/
|
||||
class Node extends DataFlow::Node {
|
||||
Node() { exists(SourceNode src | src.flowsTo(this)) }
|
||||
DataFlow::LocalSourceNode actionController() {
|
||||
result = configSource().getAMethodCall("action_controller")
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to the ActionController config object.
|
||||
* Gets a reference to the ActionDispatch config object.
|
||||
*/
|
||||
class ActionControllerNode extends DataFlow::Node {
|
||||
ActionControllerNode() {
|
||||
exists(DataFlow::CallNode source |
|
||||
source.getReceiver() instanceof Node and
|
||||
source.getMethodName() = "action_controller"
|
||||
|
|
||||
source.flowsTo(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to the ActionDispatch config object.
|
||||
*/
|
||||
class ActionDispatchNode extends DataFlow::Node {
|
||||
ActionDispatchNode() {
|
||||
exists(DataFlow::CallNode source |
|
||||
source.getReceiver() instanceof Node and
|
||||
source.getMethodName() = "action_dispatch"
|
||||
|
|
||||
source.flowsTo(this)
|
||||
)
|
||||
}
|
||||
DataFlow::LocalSourceNode actionDispatch() {
|
||||
result = configSource().getAMethodCall("action_dispatch")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,24 +161,18 @@ private module Settings {
|
||||
loc.getFile().getStem() = "test"
|
||||
}
|
||||
|
||||
private class Setting extends DataFlow::CallNode {
|
||||
private class Setting extends DataFlow::SetterCallNode {
|
||||
Setting() {
|
||||
// exclude some test configuration
|
||||
not isInTestConfiguration(this.getLocation()) and
|
||||
this.getReceiver+() instanceof Config::Node and
|
||||
this.asExpr().getExpr() instanceof SetterMethodCall
|
||||
this = Config::configSource().getAMethodCall+()
|
||||
}
|
||||
}
|
||||
|
||||
private class LiteralSetting extends Setting {
|
||||
ConstantValue value;
|
||||
|
||||
LiteralSetting() {
|
||||
exists(DataFlow::LocalSourceNode lsn |
|
||||
lsn.asExpr().getConstantValue() = value and
|
||||
lsn.flowsTo(this.getArgument(0))
|
||||
)
|
||||
}
|
||||
LiteralSetting() { value = this.getArgument(0).getALocalSource().getConstantValue() }
|
||||
|
||||
string getValueText() { result = value.toString() }
|
||||
|
||||
@@ -262,8 +217,7 @@ private module Settings {
|
||||
private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
|
||||
CsrfProtectionSetting::Range {
|
||||
AllowForgeryProtectionSetting() {
|
||||
this.getReceiver() instanceof Config::ActionControllerNode and
|
||||
this.getMethodName() = "allow_forgery_protection="
|
||||
this = Config::actionController().getAMethodCall("allow_forgery_protection=")
|
||||
}
|
||||
|
||||
override boolean getVerificationSetting() { result = this.getValue() }
|
||||
@@ -277,8 +231,7 @@ private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
|
||||
private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
|
||||
CookieSecurityConfigurationSetting::Range {
|
||||
EncryptedCookieCipherSetting() {
|
||||
this.getReceiver() instanceof Config::ActionDispatchNode and
|
||||
this.getMethodName() = "encrypted_cookie_cipher="
|
||||
this = Config::actionDispatch().getAMethodCall("encrypted_cookie_cipher=")
|
||||
}
|
||||
|
||||
OpenSslCipher getCipher() { this.getValueText() = result.getName() }
|
||||
@@ -298,8 +251,7 @@ private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
|
||||
private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanSetting,
|
||||
CookieSecurityConfigurationSetting::Range {
|
||||
UseAuthenticatedCookieEncryptionSetting() {
|
||||
this.getReceiver() instanceof Config::ActionDispatchNode and
|
||||
this.getMethodName() = "use_authenticated_cookie_encryption="
|
||||
this = Config::actionDispatch().getAMethodCall("use_authenticated_cookie_encryption=")
|
||||
}
|
||||
|
||||
boolean getDefaultValue() { result = true }
|
||||
@@ -321,8 +273,7 @@ private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanS
|
||||
private class CookiesSameSiteProtectionSetting extends Settings::NillableStringlikeSetting,
|
||||
CookieSecurityConfigurationSetting::Range {
|
||||
CookiesSameSiteProtectionSetting() {
|
||||
this.getReceiver() instanceof Config::ActionDispatchNode and
|
||||
this.getMethodName() = "cookies_same_site_protection="
|
||||
this = Config::actionDispatch().getAMethodCall("cookies_same_site_protection=")
|
||||
}
|
||||
|
||||
string getDefaultValue() { result = "lax" }
|
||||
|
||||
@@ -12,26 +12,15 @@ private import codeql.ruby.DataFlow
|
||||
* Modeling for `railties`.
|
||||
*/
|
||||
module Railties {
|
||||
private class IncludeOrPrependCall extends MethodCall {
|
||||
IncludeOrPrependCall() { this.getMethodName() = ["include", "prepend"] }
|
||||
private DataFlow::ConstRef generatorsActionsConst() {
|
||||
result = DataFlow::getConst("Rails").getConst("Generators").getConst("Actions")
|
||||
}
|
||||
|
||||
/**
|
||||
* A class which `include`s `Rails::Generators::Actions`.
|
||||
* Gets a class which is a descendent of `Rails::Generators::Actions`.
|
||||
*/
|
||||
private class GeneratorsActionsContext extends ClassDeclaration {
|
||||
GeneratorsActionsContext() {
|
||||
exists(IncludeOrPrependCall i |
|
||||
i.getEnclosingModule() = this and
|
||||
i.getArgument(0) =
|
||||
API::getTopLevelMember("Rails")
|
||||
.getMember("Generators")
|
||||
.getMember("Actions")
|
||||
.getAValueReachableFromSource()
|
||||
.asExpr()
|
||||
.getExpr()
|
||||
)
|
||||
}
|
||||
private DataFlow::ClassNode generatorsActionsClass() {
|
||||
result = generatorsActionsConst().getADescendentModule()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -40,8 +29,7 @@ module Railties {
|
||||
*/
|
||||
private class ExecuteCommandCall extends SystemCommandExecution::Range, DataFlow::CallNode {
|
||||
ExecuteCommandCall() {
|
||||
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and
|
||||
this.getMethodName() = "execute_command"
|
||||
this = generatorsActionsClass().getAnInstanceSelf().getAMethodCall("execute_command")
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) }
|
||||
@@ -54,8 +42,10 @@ module Railties {
|
||||
*/
|
||||
private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range, DataFlow::CallNode {
|
||||
ExecuteCommandWrapperCall() {
|
||||
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and
|
||||
this.getMethodName() = ["rake", "rails_command", "git"]
|
||||
this =
|
||||
generatorsActionsClass()
|
||||
.getAnInstanceSelf()
|
||||
.getAMethodCall(["rake", "rails_command", "git"])
|
||||
}
|
||||
|
||||
override DataFlow::Node getAnArgument() { result = this.getArgument(0) }
|
||||
|
||||
@@ -88,11 +88,7 @@ module UnsafeDeserialization {
|
||||
|
||||
private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
|
||||
p.getKey().getConstantValue().isStringlikeValue("mode") and
|
||||
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value |
|
||||
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
|
||||
symbolLiteral.flowsTo(value) and
|
||||
value.asExpr() = p.getValue()
|
||||
)
|
||||
DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().isSymbol(modeValue)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -180,11 +180,10 @@ private module Shared {
|
||||
private predicate isFlowFromLocals0(
|
||||
CfgNodes::ExprNodes::ElementReferenceCfgNode refNode, string hashKey, ErbFile erb
|
||||
) {
|
||||
exists(DataFlow::Node argNode, CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode |
|
||||
exists(DataFlow::Node argNode |
|
||||
argNode.asExpr() = refNode.getArgument(0) and
|
||||
refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and
|
||||
argNode.getALocalSource() = DataFlow::exprNode(strNode) and
|
||||
strNode.getExpr().getConstantValue().isStringlikeValue(hashKey) and
|
||||
argNode.getALocalSource().getConstantValue().isStringlikeValue(hashKey) and
|
||||
erb = refNode.getFile()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-all
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups: ruby
|
||||
extractor: ruby
|
||||
dbscheme: ruby.dbscheme
|
||||
|
||||
@@ -17,18 +17,11 @@ import codeql.ruby.frameworks.ActionController
|
||||
import DataFlow::PathGraph
|
||||
|
||||
/**
|
||||
* A call to `request` in an ActionController controller class.
|
||||
* Gets a call to `request` in an ActionController controller class.
|
||||
* This probably refers to the incoming HTTP request object.
|
||||
*/
|
||||
class ActionControllerRequest extends DataFlow::Node {
|
||||
ActionControllerRequest() {
|
||||
exists(DataFlow::CallNode c |
|
||||
c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and
|
||||
c.getMethodName() = "request"
|
||||
|
|
||||
c.flowsTo(this)
|
||||
)
|
||||
}
|
||||
DataFlow::LocalSourceNode request() {
|
||||
result = any(ActionControllerClass cls).getSelf().getAMethodCall("request")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -36,9 +29,11 @@ class ActionControllerRequest extends DataFlow::Node {
|
||||
*/
|
||||
class WeakParams extends DataFlow::CallNode {
|
||||
WeakParams() {
|
||||
this.getReceiver() instanceof ActionControllerRequest and
|
||||
this.getMethodName() =
|
||||
["path_parameters", "query_parameters", "request_parameters", "GET", "POST"]
|
||||
this =
|
||||
request()
|
||||
.getAMethodCall([
|
||||
"path_parameters", "query_parameters", "request_parameters", "GET", "POST"
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
name: codeql/ruby-queries
|
||||
version: 0.4.2
|
||||
version: 0.4.3-dev
|
||||
groups:
|
||||
- ruby
|
||||
- queries
|
||||
|
||||
@@ -20,7 +20,7 @@ import codeql.ruby.ast.Literal
|
||||
from AmbiguousPathCall call
|
||||
where
|
||||
// there is not a constant string argument
|
||||
not exists(call.getPathArgument().asExpr().getExpr().getConstantValue()) and
|
||||
not exists(call.getPathArgument().getConstantValue()) and
|
||||
// if it's a format string, then the first argument is not a constant string
|
||||
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
|
||||
instanceof StringTextComponent
|
||||
|
||||
@@ -2833,6 +2833,29 @@ operations/operations.rb:
|
||||
# 96| getStmt: [AssignMulExpr] ... *= ...
|
||||
# 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var
|
||||
# 96| getAnOperand/getRightOperand: [IntegerLiteral] 6
|
||||
# 98| getStmt: [AssignExpr] ... = ...
|
||||
# 98| getAnOperand/getLeftOperand: [ConstantAssignment] CONSTANT1
|
||||
# 98| getAnOperand/getRightOperand: [IntegerLiteral] 5
|
||||
# 99| getStmt: [AssignAddExpr] ... += ...
|
||||
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
|
||||
# 99| getAnOperand/getRightOperand: [IntegerLiteral] 6
|
||||
# 100| getStmt: [AssignLogicalOrExpr] ... ||= ...
|
||||
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
|
||||
# 100| getAnOperand/getRightOperand: [IntegerLiteral] 7
|
||||
# 101| getStmt: [AssignLogicalOrExpr] ... ||= ...
|
||||
# 101| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] MemberConstant
|
||||
# 101| getScopeExpr: [ConstantReadAccess] Foo
|
||||
# 101| getAnOperand/getRightOperand: [IntegerLiteral] 8
|
||||
# 102| getStmt: [AssignLogicalOrExpr] ... ||= ...
|
||||
# 102| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] OtherConstant
|
||||
# 102| getScopeExpr: [MethodCall] call to bar
|
||||
# 102| getReceiver: [MethodCall] call to foo
|
||||
# 102| getReceiver: [SelfVariableAccess] self
|
||||
# 102| getArgument: [IntegerLiteral] 1
|
||||
# 102| getAnOperand/getRightOperand: [IntegerLiteral] 7
|
||||
# 103| getStmt: [AssignLogicalOrExpr] ... ||= ...
|
||||
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
|
||||
# 103| getAnOperand/getRightOperand: [IntegerLiteral] 7
|
||||
params/params.rb:
|
||||
# 1| [Toplevel] params.rb
|
||||
# 4| getStmt: [Method] identifier_method_params
|
||||
|
||||
@@ -865,6 +865,51 @@ operations/operations.rb:
|
||||
# 96| getAnOperand/getRightOperand: [MulExpr] ... * ...
|
||||
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
|
||||
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
|
||||
# 99| [AssignAddExpr] ... += ...
|
||||
# 99| getDesugared: [AssignExpr] ... = ...
|
||||
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
|
||||
# 99| getAnOperand/getRightOperand: [AddExpr] ... + ...
|
||||
# 99| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT2
|
||||
# 99| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
|
||||
# 100| [AssignLogicalOrExpr] ... ||= ...
|
||||
# 100| getDesugared: [AssignExpr] ... = ...
|
||||
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
|
||||
# 100| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
|
||||
# 100| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT3
|
||||
# 100| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
|
||||
# 101| [AssignLogicalOrExpr] ... ||= ...
|
||||
# 101| getDesugared: [StmtSequence] ...
|
||||
# 101| getStmt: [AssignExpr] ... = ...
|
||||
# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo
|
||||
# 101| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
|
||||
# 101| getStmt: [AssignExpr] ... = ...
|
||||
# 101| getAnOperand/getLeftOperand: [ConstantAssignment] MemberConstant
|
||||
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
|
||||
# 101| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
|
||||
# 101| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] MemberConstant
|
||||
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
|
||||
# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 8
|
||||
# 102| [AssignLogicalOrExpr] ... ||= ...
|
||||
# 102| getDesugared: [StmtSequence] ...
|
||||
# 102| getStmt: [AssignExpr] ... = ...
|
||||
# 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
|
||||
# 102| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
|
||||
# 102| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] OtherConstant
|
||||
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
|
||||
# 102| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
|
||||
# 103| [AssignLogicalOrExpr] ... ||= ...
|
||||
# 103| getDesugared: [AssignExpr] ... = ...
|
||||
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
|
||||
# 103| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
|
||||
# 103| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT4
|
||||
# 103| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
|
||||
params/params.rb:
|
||||
# 8| [HashLiteral] {...}
|
||||
# 8| getDesugared: [MethodCall] call to []
|
||||
|
||||
@@ -5537,6 +5537,46 @@ operations/operations.rb:
|
||||
# 96| 0: [GlobalVariable] $global_var
|
||||
# 96| 1: [ReservedWord] *=
|
||||
# 96| 2: [Integer] 6
|
||||
# 98| 66: [Assignment] Assignment
|
||||
# 98| 0: [Constant] CONSTANT1
|
||||
# 98| 1: [ReservedWord] =
|
||||
# 98| 2: [Integer] 5
|
||||
# 99| 67: [OperatorAssignment] OperatorAssignment
|
||||
# 99| 0: [Constant] CONSTANT2
|
||||
# 99| 1: [ReservedWord] +=
|
||||
# 99| 2: [Integer] 6
|
||||
# 100| 68: [OperatorAssignment] OperatorAssignment
|
||||
# 100| 0: [Constant] CONSTANT3
|
||||
# 100| 1: [ReservedWord] ||=
|
||||
# 100| 2: [Integer] 7
|
||||
# 101| 69: [OperatorAssignment] OperatorAssignment
|
||||
# 101| 0: [ScopeResolution] ScopeResolution
|
||||
# 101| 0: [Constant] Foo
|
||||
# 101| 1: [ReservedWord] ::
|
||||
# 101| 2: [Constant] MemberConstant
|
||||
# 101| 1: [ReservedWord] ||=
|
||||
# 101| 2: [Integer] 8
|
||||
# 102| 70: [OperatorAssignment] OperatorAssignment
|
||||
# 102| 0: [ScopeResolution] ScopeResolution
|
||||
# 102| 0: [Call] Call
|
||||
# 102| 0: [Call] Call
|
||||
# 102| 0: [Identifier] foo
|
||||
# 102| 1: [ArgumentList] ArgumentList
|
||||
# 102| 0: [ReservedWord] (
|
||||
# 102| 1: [Integer] 1
|
||||
# 102| 2: [ReservedWord] )
|
||||
# 102| 1: [ReservedWord] .
|
||||
# 102| 2: [Identifier] bar
|
||||
# 102| 1: [ReservedWord] ::
|
||||
# 102| 2: [Constant] OtherConstant
|
||||
# 102| 1: [ReservedWord] ||=
|
||||
# 102| 2: [Integer] 7
|
||||
# 103| 71: [OperatorAssignment] OperatorAssignment
|
||||
# 103| 0: [ScopeResolution] ScopeResolution
|
||||
# 103| 0: [ReservedWord] ::
|
||||
# 103| 1: [Constant] CONSTANT4
|
||||
# 103| 1: [ReservedWord] ||=
|
||||
# 103| 2: [Integer] 7
|
||||
# 1| [Comment] # Start with assignments to all the identifiers used below, so that they are
|
||||
# 2| [Comment] # interpreted as variables.
|
||||
# 22| [Comment] # Unary operations
|
||||
|
||||
@@ -888,6 +888,13 @@ exprValue
|
||||
| operations/operations.rb:92:10:92:10 | 4 | 4 | int |
|
||||
| operations/operations.rb:95:15:95:15 | 5 | 5 | int |
|
||||
| operations/operations.rb:96:16:96:16 | 6 | 6 | int |
|
||||
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
|
||||
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
|
||||
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
|
||||
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
|
||||
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
|
||||
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
|
||||
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
|
||||
| params/params.rb:41:46:41:46 | 7 | 7 | int |
|
||||
| params/params.rb:47:19:47:21 | :bar | :bar | symbol |
|
||||
| params/params.rb:47:24:47:24 | 2 | 2 | int |
|
||||
@@ -1764,6 +1771,13 @@ exprCfgNodeValue
|
||||
| operations/operations.rb:92:10:92:10 | 4 | 4 | int |
|
||||
| operations/operations.rb:95:15:95:15 | 5 | 5 | int |
|
||||
| operations/operations.rb:96:16:96:16 | 6 | 6 | int |
|
||||
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
|
||||
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
|
||||
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
|
||||
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
|
||||
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
|
||||
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
|
||||
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
|
||||
| params/params.rb:41:46:41:46 | 7 | 7 | int |
|
||||
| params/params.rb:47:19:47:21 | :bar | :bar | symbol |
|
||||
| params/params.rb:47:24:47:24 | 2 | 2 | int |
|
||||
|
||||
@@ -52,6 +52,19 @@ assignments
|
||||
| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr |
|
||||
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
|
||||
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
|
||||
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | operations.rb:98:13:98:13 | 5 | AssignExpr |
|
||||
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
|
||||
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
|
||||
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | operations.rb:101:1:101:3 | Foo | AssignExpr |
|
||||
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
|
||||
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | operations.rb:102:1:102:10 | call to bar | AssignExpr |
|
||||
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
|
||||
assignOperations
|
||||
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
|
||||
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
|
||||
@@ -69,6 +82,11 @@ assignOperations
|
||||
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
|
||||
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
|
||||
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
|
||||
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
|
||||
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
|
||||
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
|
||||
assignArithmeticOperations
|
||||
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
|
||||
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
|
||||
@@ -79,9 +97,14 @@ assignArithmeticOperations
|
||||
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
|
||||
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
|
||||
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
|
||||
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
|
||||
assignLogicalOperations
|
||||
| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr |
|
||||
| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr |
|
||||
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
|
||||
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
|
||||
assignBitwiseOperations
|
||||
| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr |
|
||||
| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr |
|
||||
|
||||
@@ -40,6 +40,11 @@ binaryOperations
|
||||
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
|
||||
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
|
||||
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
|
||||
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
|
||||
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
|
||||
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
|
||||
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
|
||||
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
|
||||
binaryArithmeticOperations
|
||||
| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr |
|
||||
| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr |
|
||||
@@ -56,6 +61,7 @@ binaryArithmeticOperations
|
||||
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
|
||||
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
|
||||
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
|
||||
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
|
||||
binaryLogicalOperations
|
||||
| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr |
|
||||
| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr |
|
||||
@@ -63,6 +69,10 @@ binaryLogicalOperations
|
||||
| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr |
|
||||
| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr |
|
||||
| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr |
|
||||
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
|
||||
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
|
||||
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
|
||||
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
|
||||
binaryBitwiseOperations
|
||||
| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr |
|
||||
| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr |
|
||||
|
||||
@@ -194,3 +194,39 @@
|
||||
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
|
||||
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr |
|
||||
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr |
|
||||
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | AssignExpr |
|
||||
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:13:98:13 | 5 | AssignExpr |
|
||||
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | AssignAddExpr |
|
||||
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
|
||||
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | AssignExpr |
|
||||
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
|
||||
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | AddExpr |
|
||||
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:14:99:14 | 6 | AddExpr |
|
||||
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | AssignExpr |
|
||||
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | AssignLogicalOrExpr |
|
||||
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | LogicalOrExpr |
|
||||
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
|
||||
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | Foo | AssignExpr |
|
||||
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | AssignExpr |
|
||||
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | AssignExpr |
|
||||
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | AssignLogicalOrExpr |
|
||||
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
|
||||
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | LogicalOrExpr |
|
||||
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
|
||||
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | AssignExpr |
|
||||
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | call to bar | AssignExpr |
|
||||
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | AssignExpr |
|
||||
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | AssignLogicalOrExpr |
|
||||
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | LogicalOrExpr |
|
||||
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
|
||||
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | AssignExpr |
|
||||
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
|
||||
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | AssignLogicalOrExpr |
|
||||
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
|
||||
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | LogicalOrExpr |
|
||||
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
|
||||
|
||||
@@ -94,3 +94,10 @@ end
|
||||
|
||||
$global_var = 5
|
||||
$global_var *= 6
|
||||
|
||||
CONSTANT1 = 5
|
||||
CONSTANT2 += 6
|
||||
CONSTANT3 ||= 7
|
||||
Foo::MemberConstant ||= 8
|
||||
foo(1).bar::OtherConstant ||= 7
|
||||
::CONSTANT4 ||= 7
|
||||
|
||||
@@ -3762,6 +3762,81 @@ cfg.rb:
|
||||
# 215| self
|
||||
#-----| -> call to something_else
|
||||
|
||||
constant_compound_assign.rb:
|
||||
# 1| Foo
|
||||
#-----| -> foo_before
|
||||
|
||||
# 1| enter constant_compound_assign.rb
|
||||
#-----| -> Foo
|
||||
|
||||
# 1| exit constant_compound_assign.rb
|
||||
|
||||
# 1| exit constant_compound_assign.rb (normal)
|
||||
#-----| -> exit constant_compound_assign.rb
|
||||
|
||||
# 2| foo_before
|
||||
#-----| -> FOO_CONSTANT
|
||||
|
||||
# 5| FOO_CONSTANT
|
||||
#-----| -> FOO_CONSTANT
|
||||
|
||||
# 5| FOO_CONSTANT
|
||||
#-----| true -> ... || ...
|
||||
#-----| false -> 123
|
||||
|
||||
# 5| ... = ...
|
||||
#-----| -> foo_after
|
||||
|
||||
# 5| ... || ...
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 5| 123
|
||||
#-----| -> ... || ...
|
||||
|
||||
# 7| foo_after
|
||||
#-----| -> top_before
|
||||
|
||||
# 11| top_before
|
||||
#-----| -> TOP_CONSTANT
|
||||
|
||||
# 14| TOP_CONSTANT
|
||||
#-----| -> TOP_CONSTANT
|
||||
|
||||
# 14| TOP_CONSTANT
|
||||
#-----| true -> ... || ...
|
||||
#-----| false -> 123
|
||||
|
||||
# 14| ... = ...
|
||||
#-----| -> top_after
|
||||
|
||||
# 14| ... || ...
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 14| 123
|
||||
#-----| -> ... || ...
|
||||
|
||||
# 16| top_after
|
||||
#-----| -> TOP_CONSTANT2
|
||||
|
||||
# 19| TOP_CONSTANT2
|
||||
#-----| -> TOP_CONSTANT2
|
||||
|
||||
# 19| TOP_CONSTANT2
|
||||
#-----| true -> ... || ...
|
||||
#-----| false -> 123
|
||||
|
||||
# 19| ... = ...
|
||||
#-----| -> top_after2
|
||||
|
||||
# 19| ... || ...
|
||||
#-----| -> ... = ...
|
||||
|
||||
# 19| 123
|
||||
#-----| -> ... || ...
|
||||
|
||||
# 21| top_after2
|
||||
#-----| -> exit constant_compound_assign.rb (normal)
|
||||
|
||||
desugar.rb:
|
||||
# 1| enter m1
|
||||
#-----| -> x
|
||||
|
||||
@@ -212,6 +212,9 @@ positionalArguments
|
||||
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:10:205:10 | 1 |
|
||||
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:12:205:12 | 2 |
|
||||
| cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 |
|
||||
| constant_compound_assign.rb:5:18:5:20 | ... \|\| ... | constant_compound_assign.rb:5:22:5:24 | 123 |
|
||||
| constant_compound_assign.rb:14:14:14:16 | ... \|\| ... | constant_compound_assign.rb:14:18:14:20 | 123 |
|
||||
| constant_compound_assign.rb:19:17:19:19 | ... \|\| ... | constant_compound_assign.rb:19:21:19:23 | 123 |
|
||||
| desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 |
|
||||
| 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 |
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
module Foo
|
||||
def foo_before
|
||||
end
|
||||
|
||||
FOO_CONSTANT ||= 123
|
||||
|
||||
def foo_after
|
||||
end
|
||||
end
|
||||
|
||||
def top_before
|
||||
end
|
||||
|
||||
TOP_CONSTANT ||= 123
|
||||
|
||||
def top_after
|
||||
end
|
||||
|
||||
::TOP_CONSTANT2 ||= 123
|
||||
|
||||
def top_after2
|
||||
end
|
||||
@@ -3,7 +3,6 @@
|
||||
| array_flow.rb:180:28:180:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 |
|
||||
| array_flow.rb:226:10:226:13 | ...[...] | Unexpected result: hasValueFlow=25 |
|
||||
| array_flow.rb:242:14:242:14 | x | Unexpected result: hasValueFlow=27.2 |
|
||||
| array_flow.rb:255:16:255:56 | # $ hasValueFlow=28.1 $ hasValueFlow=28.2 | Missing result:hasValueFlow=28.1 |
|
||||
| array_flow.rb:319:10:319:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
|
||||
| array_flow.rb:320:10:320:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
|
||||
| array_flow.rb:321:10:321:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
|
||||
@@ -26,7 +25,6 @@
|
||||
| array_flow.rb:490:10:490:13 | ...[...] | Unexpected result: hasValueFlow=54.5 |
|
||||
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.2 |
|
||||
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.3 |
|
||||
| array_flow.rb:564:16:564:56 | # $ hasValueFlow=62.1 $ hasValueFlow=62.2 | Missing result:hasValueFlow=62.1 |
|
||||
| array_flow.rb:575:16:575:34 | # $ hasValueFlow=63 | Missing result:hasValueFlow=63 |
|
||||
| array_flow.rb:580:19:580:37 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
|
||||
| array_flow.rb:582:16:582:34 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
failures
|
||||
edges
|
||||
| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
|
||||
| captured_variables.rb:1:24:1:24 | x : | captured_variables.rb:2:20:2:20 | x |
|
||||
| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
|
||||
| captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:1:24:1:24 | x : |
|
||||
| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
|
||||
| captured_variables.rb:21:33:21:33 | x : | captured_variables.rb:23:14:23:14 | x |
|
||||
| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
|
||||
| captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:21:33:21:33 | x : |
|
||||
| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
|
||||
| captured_variables.rb:32:31:32:31 | x : | captured_variables.rb:34:14:34:14 | x |
|
||||
| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
|
||||
| captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:32:31:32:31 | x : |
|
||||
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
|
||||
| instance_variables.rb:10:19:10:19 | x : | instance_variables.rb:11:18:11:18 | x : |
|
||||
| instance_variables.rb:11:18:11:18 | x : | instance_variables.rb:11:9:11:14 | [post] self [@field] : |
|
||||
@@ -152,6 +164,24 @@ edges
|
||||
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
|
||||
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:84:6:84:20 | call to get_field |
|
||||
nodes
|
||||
| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:1:24:1:24 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
|
||||
| captured_variables.rb:2:20:2:20 | x | semmle.label | x |
|
||||
| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
|
||||
| captured_variables.rb:5:20:5:30 | call to source : | semmle.label | call to source : |
|
||||
| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:21:33:21:33 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
|
||||
| captured_variables.rb:23:14:23:14 | x | semmle.label | x |
|
||||
| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
|
||||
| captured_variables.rb:27:29:27:39 | call to source : | semmle.label | call to source : |
|
||||
| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:32:31:32:31 | x : | semmle.label | x : |
|
||||
| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
|
||||
| captured_variables.rb:34:14:34:14 | x | semmle.label | x |
|
||||
| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
|
||||
| captured_variables.rb:38:27:38:37 | call to source : | semmle.label | call to source : |
|
||||
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
|
||||
| instance_variables.rb:10:19:10:19 | x : | semmle.label | x : |
|
||||
| instance_variables.rb:11:9:11:14 | [post] self [@field] : | semmle.label | [post] self [@field] : |
|
||||
@@ -335,6 +365,9 @@ subpaths
|
||||
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
|
||||
| instance_variables.rb:84:6:84:10 | foo13 [@field] : | instance_variables.rb:13:5:15:7 | self in get_field [@field] : | instance_variables.rb:14:9:14:21 | return : | instance_variables.rb:84:6:84:20 | call to get_field |
|
||||
#select
|
||||
| captured_variables.rb:2:20:2:20 | x | captured_variables.rb:5:20:5:30 | call to source : | captured_variables.rb:2:20:2:20 | x | $@ | captured_variables.rb:5:20:5:30 | call to source : | call to source : |
|
||||
| captured_variables.rb:23:14:23:14 | x | captured_variables.rb:27:29:27:39 | call to source : | captured_variables.rb:23:14:23:14 | x | $@ | captured_variables.rb:27:29:27:39 | call to source : | call to source : |
|
||||
| captured_variables.rb:34:14:34:14 | x | captured_variables.rb:38:27:38:37 | call to source : | captured_variables.rb:34:14:34:14 | x | $@ | captured_variables.rb:38:27:38:37 | call to source : | call to source : |
|
||||
| instance_variables.rb:20:10:20:13 | @foo | instance_variables.rb:19:12:19:21 | call to taint : | instance_variables.rb:20:10:20:13 | @foo | $@ | instance_variables.rb:19:12:19:21 | call to taint : | call to taint : |
|
||||
| instance_variables.rb:25:6:25:18 | call to get_field | instance_variables.rb:24:15:24:23 | call to taint : | instance_variables.rb:25:6:25:18 | call to get_field | $@ | instance_variables.rb:24:15:24:23 | call to taint : | call to taint : |
|
||||
| instance_variables.rb:29:6:29:18 | call to inc_field | instance_variables.rb:28:15:28:22 | call to taint : | instance_variables.rb:29:6:29:18 | call to inc_field | $@ | instance_variables.rb:28:15:28:22 | call to taint : | call to taint : |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user