Merge remote-tracking branch 'origin/main' into nickrolfe/active_support_flow_summaries

This commit is contained in:
Nick Rolfe
2022-11-09 17:02:05 +00:00
183 changed files with 3953 additions and 1019 deletions

View File

@@ -2,11 +2,11 @@ name: "Compile all queries using the latest stable CodeQL CLI"
on:
push:
branches: [main] # makes sure the cache gets populated
pull_request:
branches:
branches: # makes sure the cache gets populated - running on the branches people tend to merge into.
- main
- "rc/*"
- "codeql-cli-*"
pull_request:
jobs:
compile-queries:
@@ -14,31 +14,33 @@ jobs:
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
# calculate the merge-base with main, in a way that works both on PRs and pushes to main.
- name: Calculate merge-base
if: ${{ github.event_name == 'pull_request' }}
env:
BASE_BRANCH: ${{ github.base_ref }}
run: |
MERGE_BASE=$(git merge-base --fork-point origin/$BASE_BRANCH)
MERGE_BASE=$(git cat-file commit $GITHUB_SHA | grep '^parent ' | head -1 | cut -f 2 -d " ")
echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
- name: Calculate merge-base - branch
if: ${{ github.event_name != 'pull_request' }}
# using github.sha instead, since we're directly on a branch, and not in a PR
run: |
MERGE_BASE=${{ github.sha }}
echo "merge-base=$MERGE_BASE" >> $GITHUB_ENV
- name: Cache CodeQL query compilation
- name: Read CodeQL query compilation - PR
if: ${{ github.event_name == 'pull_request' }}
uses: actions/cache@v3
with:
path: '*/ql/src/.cache'
# current GH HEAD first, merge-base second, generic third
key: codeql-stable-compile-${{ github.sha }}
key: codeql-compile-pr-${{ github.sha }} # deliberately not using the `compile-compile-main` keys here.
restore-keys: |
codeql-stable-compile-${{ env.merge-base }}
codeql-stable-compile-
codeql-compile-${{ github.base_ref }}-${{ env.merge-base }}
codeql-compile-${{ github.base_ref }}-
codeql-compile-main-
- name: Fill CodeQL query compilation cache - main
if: ${{ github.event_name != 'pull_request' }}
uses: actions/cache@v3
with:
path: '*/ql/src/.cache'
key: codeql-compile-${{ github.ref_name }}-${{ github.sha }} # just fill on main
restore-keys: | # restore from another random commit, to speed up compilation.
codeql-compile-${{ github.ref_name }}-
codeql-compile-main-
- name: Setup CodeQL
uses: ./.github/actions/fetch-codeql
with:

View File

@@ -51,12 +51,14 @@ jobs:
- uses: actions/checkout@v3
- uses: ./swift/actions/create-extractor-pack
- uses: ./swift/actions/run-quick-tests
- uses: ./swift/actions/print-unextracted
build-and-test-linux:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- uses: ./swift/actions/create-extractor-pack
- uses: ./swift/actions/run-quick-tests
- uses: ./swift/actions/print-unextracted
qltests-linux:
needs: build-and-test-linux
runs-on: ubuntu-latest

View File

@@ -50,19 +50,18 @@ class StrcatFunction extends TaintFunction, DataFlowFunction, ArrayFunction, Sid
}
override predicate hasTaintFlow(FunctionInput input, FunctionOutput output) {
this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
input.isParameter(2) and
output.isParameterDeref(0)
or
this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
input.isParameter(3) and
output.isParameterDeref(0)
or
input.isParameterDeref(0) and
output.isParameterDeref(0)
or
input.isParameterDeref(1) and
output.isParameterDeref(0)
(
this.getName() = ["strncat", "wcsncat", "_mbsncat", "_mbsncat_l"] and
input.isParameter(2)
or
this.getName() = ["_mbsncat_l", "_mbsnbcat_l"] and
input.isParameter(3)
or
input.isParameterDeref(0)
or
input.isParameterDeref(1)
) and
(output.isParameterDeref(0) or output.isReturnValueDeref())
}
override predicate hasArrayInput(int param) {

View File

@@ -13,11 +13,18 @@
import cpp
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
where
ffc = fl.getUse() and
expected = fl.getNumArgNeeded() and
given = ffc.getNumFormatArgument() and
expected < given and
fl.specsAreKnown()
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
fl.specsAreKnown() and
(
if ffc.isInMacroExpansion()
then ffcName = ffc.getTarget().getName() + " (in a macro expansion)"
else ffcName = ffc.getTarget().getName()
)
select ffc,
"Format for " + ffcName + " expects " + expected.toString() + " arguments but given " +
given.toString()

View File

@@ -16,11 +16,18 @@
import cpp
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given
from FormatLiteral fl, FormattingFunctionCall ffc, int expected, int given, string ffcName
where
ffc = fl.getUse() and
expected = fl.getNumArgNeeded() and
given = ffc.getNumFormatArgument() and
expected > given and
fl.specsAreKnown()
select ffc, "Format expects " + expected.toString() + " arguments but given " + given.toString()
fl.specsAreKnown() and
(
if ffc.isInMacroExpansion()
then ffcName = ffc.getTarget().getName() + " (in a macro expansion)"
else ffcName = ffc.getTarget().getName()
)
select ffc,
"Format for " + ffcName + " expects " + expected.toString() + " arguments but given " +
given.toString()

View File

@@ -87,4 +87,7 @@ postWithInFlow
| test.cpp:465:3:465:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:465:4:465:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:470:22:470:22 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:499:3:499:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:499:4:499:4 | p [inner post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:505:35:505:35 | x [inner post update] | PostUpdateNode should not be the target of local flow. |
viableImplInCallContextTooLarge

View File

@@ -582,6 +582,13 @@ postWithInFlow
| test.cpp:489:7:489:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:491:5:491:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:494:5:494:5 | x [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:499:3:499:4 | * ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:499:4:499:4 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:499:4:499:4 | p [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:504:7:504:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:505:34:505:35 | & ... [post update] | PostUpdateNode should not be the target of local flow. |
| test.cpp:505:35:505:35 | x [post update] | PostUpdateNode should not be the target of local flow. |
| true_upon_entry.cpp:9:7:9:7 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| true_upon_entry.cpp:10:12:10:12 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |
| true_upon_entry.cpp:10:27:10:27 | VariableAddress [post update] | PostUpdateNode should not be the target of local flow. |

View File

@@ -494,3 +494,14 @@ void regression_with_phi_flow(int clean1) {
x = source();
}
}
int intOutparamSourceMissingReturn(int *p) {
*p = source();
// return deliberately omitted to test IR dataflow behavior
}
void viaOutparamMissingReturn() {
int x = 0;
intOutparamSourceMissingReturn(&x);
sink(x); // $ ast,ir
}

View File

@@ -5964,6 +5964,7 @@
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:3:172:8 | call to strcat | |
| taint.cpp:172:10:172:15 | buffer | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
| taint.cpp:172:10:172:15 | ref arg buffer | taint.cpp:173:8:173:13 | buffer | |
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:3:172:8 | call to strcat | TAINT |
| taint.cpp:172:18:172:24 | tainted | taint.cpp:172:10:172:15 | ref arg buffer | TAINT |
| taint.cpp:180:19:180:19 | p | taint.cpp:180:19:180:19 | p | |
| taint.cpp:180:19:180:19 | p | taint.cpp:181:9:181:9 | p | |
@@ -6373,12 +6374,14 @@
| taint.cpp:561:9:561:13 | dest1 | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
| taint.cpp:561:9:561:13 | ref arg dest1 | taint.cpp:562:7:562:11 | dest1 | |
| taint.cpp:561:16:561:21 | source | taint.cpp:561:2:561:7 | call to strcat | TAINT |
| taint.cpp:561:16:561:21 | source | taint.cpp:561:9:561:13 | ref arg dest1 | TAINT |
| taint.cpp:562:7:562:11 | ref arg dest1 | taint.cpp:560:24:560:28 | dest1 | |
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:2:564:7 | call to strcat | |
| taint.cpp:564:9:564:13 | dest2 | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
| taint.cpp:564:9:564:13 | ref arg dest2 | taint.cpp:565:7:565:11 | dest2 | |
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:2:564:7 | call to strcat | TAINT |
| taint.cpp:564:16:564:20 | clean | taint.cpp:564:9:564:13 | ref arg dest2 | TAINT |
| taint.cpp:565:7:565:11 | ref arg dest2 | taint.cpp:560:37:560:41 | dest2 | |
| taint.cpp:572:37:572:41 | dest1 | taint.cpp:572:37:572:41 | dest1 | |
@@ -6405,9 +6408,12 @@
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:575:7:575:11 | dest1 | |
| taint.cpp:574:36:574:40 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
| taint.cpp:574:43:574:45 | ptr | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:574:48:574:48 | n | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
| taint.cpp:574:48:574:48 | n | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:574:51:574:56 | ref arg source | taint.cpp:573:49:573:54 | source | |
| taint.cpp:574:51:574:56 | source | taint.cpp:574:25:574:34 | call to _mbsncat_l | TAINT |
| taint.cpp:574:51:574:56 | source | taint.cpp:574:36:574:40 | ref arg dest1 | TAINT |
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:572:37:572:41 | dest1 | |
| taint.cpp:575:7:575:11 | ref arg dest1 | taint.cpp:576:8:576:12 | dest1 | |
@@ -6421,8 +6427,11 @@
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:581:7:581:11 | dest3 | |
| taint.cpp:580:36:580:40 | ref arg dest3 | taint.cpp:582:8:582:12 | dest3 | |
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
| taint.cpp:580:43:580:45 | ptr | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:48:580:48 | n | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
| taint.cpp:580:48:580:48 | n | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:25:580:34 | call to _mbsncat_l | TAINT |
| taint.cpp:580:51:580:55 | clean | taint.cpp:580:36:580:40 | ref arg dest3 | TAINT |
| taint.cpp:580:51:580:55 | ref arg clean | taint.cpp:573:32:573:36 | clean | |
| taint.cpp:581:7:581:11 | ref arg dest3 | taint.cpp:572:85:572:89 | dest3 | |

View File

@@ -574,8 +574,8 @@ void test__mbsncat_l(unsigned char* dest1, unsigned const char* ptr, unsigned ch
unsigned char* dest2 = _mbsncat_l(dest1, ptr, n, source);
sink(dest1); // $ SPURIOUS: ast,ir
sink(*dest1); // $ ast,ir
sink(dest2); // $ SPURIOUS: ir
sink(*dest2); // $ ir
sink(dest2); // $ SPURIOUS: ast,ir
sink(*dest2); // $ ast,ir
unsigned char* dest4 = _mbsncat_l(dest3, ptr, n, clean);
sink(dest3);

View File

@@ -1,14 +1,14 @@
| a.c:18:3:18:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 2 |
| b.c:15:3:15:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 2 |
| c.c:7:3:7:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 2 |
| custom_printf.cpp:31:5:31:12 | call to myPrintf | Format expects 2 arguments but given 3 |
| macros.cpp:12:2:12:31 | call to printf | Format expects 2 arguments but given 3 |
| macros.cpp:16:2:16:30 | call to printf | Format expects 2 arguments but given 3 |
| test.c:7:2:7:7 | call to printf | Format expects 0 arguments but given 1 |
| test.c:21:2:21:7 | call to printf | Format expects 2 arguments but given 3 |
| test.c:27:3:27:8 | call to printf | Format expects 2 arguments but given 3 |
| test.c:31:3:31:8 | call to printf | Format expects 1 arguments but given 3 |
| test.c:32:3:32:8 | call to printf | Format expects 1 arguments but given 2 |
| test.c:39:3:39:8 | call to printf | Format expects 2 arguments but given 5 |
| test.c:40:3:40:8 | call to printf | Format expects 2 arguments but given 4 |
| test.c:41:3:41:8 | call to printf | Format expects 2 arguments but given 3 |
| a.c:18:3:18:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 2 |
| b.c:15:3:15:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 2 |
| c.c:7:3:7:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 2 |
| custom_printf.cpp:31:5:31:12 | call to myPrintf | Format for myPrintf expects 2 arguments but given 3 |
| macros.cpp:12:2:12:31 | call to printf | Format for printf (in a macro expansion) expects 2 arguments but given 3 |
| macros.cpp:16:2:16:30 | call to printf | Format for printf (in a macro expansion) expects 2 arguments but given 3 |
| test.c:7:2:7:7 | call to printf | Format for printf expects 0 arguments but given 1 |
| test.c:21:2:21:7 | call to printf | Format for printf expects 2 arguments but given 3 |
| test.c:27:3:27:8 | call to printf | Format for printf expects 2 arguments but given 3 |
| test.c:31:3:31:8 | call to printf | Format for printf expects 1 arguments but given 3 |
| test.c:32:3:32:8 | call to printf | Format for printf expects 1 arguments but given 2 |
| test.c:39:3:39:8 | call to printf | Format for printf expects 2 arguments but given 5 |
| test.c:40:3:40:8 | call to printf | Format for printf expects 2 arguments but given 4 |
| test.c:41:3:41:8 | call to printf | Format for printf expects 2 arguments but given 3 |

View File

@@ -1,11 +1,12 @@
| a.c:16:3:16:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 0 |
| b.c:13:3:13:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 0 |
| c.c:5:3:5:25 | call to myMultiplyDefinedPrintf | Format expects 1 arguments but given 0 |
| custom_printf.cpp:29:5:29:12 | call to myPrintf | Format expects 2 arguments but given 1 |
| macros.cpp:14:2:14:37 | call to printf | Format expects 4 arguments but given 3 |
| macros.cpp:21:2:21:36 | call to printf | Format expects 4 arguments but given 3 |
| test.c:9:2:9:7 | call to printf | Format expects 1 arguments but given 0 |
| test.c:12:2:12:7 | call to printf | Format expects 2 arguments but given 1 |
| test.c:15:2:15:7 | call to printf | Format expects 3 arguments but given 2 |
| test.c:19:2:19:7 | call to printf | Format expects 2 arguments but given 1 |
| test.c:29:3:29:8 | call to printf | Format expects 2 arguments but given 1 |
| a.c:16:3:16:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 0 |
| b.c:13:3:13:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 0 |
| c.c:5:3:5:25 | call to myMultiplyDefinedPrintf | Format for myMultiplyDefinedPrintf expects 1 arguments but given 0 |
| custom_printf.cpp:29:5:29:12 | call to myPrintf | Format for myPrintf expects 2 arguments but given 1 |
| macros.cpp:14:2:14:37 | call to printf | Format for printf (in a macro expansion) expects 4 arguments but given 3 |
| macros.cpp:21:2:21:36 | call to printf | Format for printf (in a macro expansion) expects 4 arguments but given 3 |
| macros.cpp:32:2:32:25 | call to printf | Format for printf (in a macro expansion) expects 1 arguments but given 0 |
| test.c:9:2:9:7 | call to printf | Format for printf expects 1 arguments but given 0 |
| test.c:12:2:12:7 | call to printf | Format for printf expects 2 arguments but given 1 |
| test.c:15:2:15:7 | call to printf | Format for printf expects 3 arguments but given 2 |
| test.c:19:2:19:7 | call to printf | Format for printf expects 2 arguments but given 1 |
| test.c:29:3:29:8 | call to printf | Format for printf expects 2 arguments but given 1 |

View File

@@ -13,10 +13,21 @@ void testMacros(int a, int b, int c)
GOODPRINTF("%i %i %i\n", a, b, c); // GOOD
GOODPRINTF("%i %i %i %i\n", a, b, c); // BAD: too few format arguments
BADPRINTF("%i %i\n", a, b, 0); // BAD: too many format arguments
BADPRINTF("%i %i\n", a, b, 0); // DUBIOUS: too many format arguments
// ^ here there are too many format arguments, but the design of the Macro forces the user
// to do this, and the extra argument is harmlessly ignored in practice. Reporting these
// results can be extremely noisy (e.g. in openldap).
BADPRINTF("%i %i %i\n", a, b, c); // GOOD
BADPRINTF("%i %i %i %i\n", a, b, c); // BAD: too few format arguments
}
#define DOTHING(x) \
printf("doing thing: " #x); x
void testMacros2()
{
int x;
DOTHING(x++); // GOOD
DOTHING(printf("%i", x)); // BAD: the printf inside the macro has too few format arguments
}

View File

@@ -8,6 +8,10 @@
import internal.CaptureModels
class Activate extends ActiveConfiguration {
override predicate activateToSinkConfig() { any() }
}
from DataFlowTargetApi api, string sink
where sink = captureSink(api)
select sink order by sink

View File

@@ -8,6 +8,10 @@
import internal.CaptureModels
class Activate extends ActiveConfiguration {
override predicate activateFromSourceConfig() { any() }
}
from DataFlowTargetApi api, string source
where source = captureSource(api)
select source order by source

View File

@@ -5,6 +5,14 @@
private import CaptureModelsSpecific
class ActiveConfiguration extends Unit {
predicate activateThroughFlowConfig() { none() }
predicate activateFromSourceConfig() { none() }
predicate activateToSinkConfig() { none() }
}
class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { isRelevantForDataFlowModels(this) }
}
@@ -140,7 +148,9 @@ private class TaintStore extends DataFlow::FlowState {
* This can be used to generate Flow summaries for APIs from parameter to return.
*/
private class ThroughFlowConfig extends TaintTracking::Configuration {
ThroughFlowConfig() { this = "ThroughFlowConfig" }
ThroughFlowConfig() {
this = "ThroughFlowConfig" and any(ActiveConfiguration ac).activateThroughFlowConfig()
}
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source instanceof DataFlow::ParameterNode and
@@ -210,7 +220,9 @@ string captureThroughFlow(DataFlowTargetApi api) {
* via its return (then the API itself becomes a source).
*/
private class FromSourceConfiguration extends TaintTracking::Configuration {
FromSourceConfiguration() { this = "FromSourceConfiguration" }
FromSourceConfiguration() {
this = "FromSourceConfiguration" and any(ActiveConfiguration ac).activateFromSourceConfig()
}
override predicate isSource(DataFlow::Node source) { ExternalFlow::sourceNode(source, _) }
@@ -250,8 +262,13 @@ string captureSource(DataFlowTargetApi api) {
* This can be used to generate Sink summaries for APIs, if the API propagates a parameter (or enclosing type field)
* into an existing known sink (then the API itself becomes a sink).
*/
private class PropagateToSinkConfiguration extends PropagateToSinkConfigurationSpecific {
PropagateToSinkConfiguration() { this = "parameters or fields flowing into sinks" }
private class PropagateToSinkConfiguration extends TaintTracking::Configuration {
PropagateToSinkConfiguration() {
this = "parameters or fields flowing into sinks" and
any(ActiveConfiguration ac).activateToSinkConfig()
}
override predicate isSource(DataFlow::Node source) { apiSource(source) }
override predicate isSink(DataFlow::Node sink) { ExternalFlow::sinkNode(sink, _) }

View File

@@ -19,6 +19,8 @@ module TaintTracking = CS::TaintTracking;
class Type = CS::Type;
class Unit = DataFlowPrivate::Unit;
/**
* Holds if any of the parameters of `api` are `System.Func<>`.
*/
@@ -174,15 +176,11 @@ private predicate isRelevantMemberAccess(DataFlow::Node node) {
}
/**
* Language specific parts of the `PropagateToSinkConfiguration`.
* Holds if `source` is an api entrypoint relevant for creating sink models.
*/
class PropagateToSinkConfigurationSpecific extends CS::TaintTracking::Configuration {
PropagateToSinkConfigurationSpecific() { this = "parameters or fields flowing into sinks" }
override predicate isSource(DataFlow::Node source) {
(isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode) and
isRelevantForModels(source.getEnclosingCallable())
}
predicate apiSource(DataFlow::Node source) {
(isRelevantMemberAccess(source) or source instanceof DataFlow::ParameterNode) and
isRelevantForModels(source.getEnclosingCallable())
}
/**

View File

@@ -1,5 +1,9 @@
private import CaptureModels
private class Activate extends ActiveConfiguration {
override predicate activateThroughFlowConfig() { any() }
}
/**
* Capture fluent APIs that return `this`.
* Example of a fluent API:

View File

@@ -88,6 +88,22 @@ class KeyPairGenerator extends RefType {
KeyPairGenerator() { this.hasQualifiedName("java.security", "KeyPairGenerator") }
}
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
class KeyGeneratorInitMethod extends Method {
KeyGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyGenerator and
this.hasName("init")
}
}
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
class KeyPairGeneratorInitMethod extends Method {
KeyPairGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyPairGenerator and
this.hasName("initialize")
}
}
/** The `verify` method of the class `javax.net.ssl.HostnameVerifier`. */
class HostnameVerifierVerify extends Method {
HostnameVerifierVerify() {
@@ -367,8 +383,8 @@ class JavaSecuritySignature extends JavaSecurityAlgoSpec {
override Expr getAlgoSpec() { result = this.(ConstructorCall).getArgument(0) }
}
/** A method call to the Java class `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaxCryptoAlgoSpec {
/** A call to the `getInstance` method declared in `java.security.KeyPairGenerator`. */
class JavaSecurityKeyPairGenerator extends JavaSecurityAlgoSpec {
JavaSecurityKeyPairGenerator() {
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof KeyPairGenerator and
@@ -378,3 +394,53 @@ class JavaSecurityKeyPairGenerator extends JavaxCryptoAlgoSpec {
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
/** The Java class `java.security.AlgorithmParameterGenerator`. */
class AlgorithmParameterGenerator extends RefType {
AlgorithmParameterGenerator() {
this.hasQualifiedName("java.security", "AlgorithmParameterGenerator")
}
}
/** The `init` method declared in `java.security.AlgorithmParameterGenerator`. */
class AlgoParamGeneratorInitMethod extends Method {
AlgoParamGeneratorInitMethod() {
this.getDeclaringType() instanceof AlgorithmParameterGenerator and
this.hasName("init")
}
}
/** A call to the `getInstance` method declared in `java.security.AlgorithmParameterGenerator`. */
class JavaSecurityAlgoParamGenerator extends JavaSecurityAlgoSpec {
JavaSecurityAlgoParamGenerator() {
exists(Method m | m.getAReference() = this |
m.getDeclaringType() instanceof AlgorithmParameterGenerator and
m.getName() = "getInstance"
)
}
override Expr getAlgoSpec() { result = this.(MethodAccess).getArgument(0) }
}
/** An implementation of the `java.security.spec.AlgorithmParameterSpec` interface. */
abstract class AlgorithmParameterSpec extends RefType { }
/** The Java class `java.security.spec.ECGenParameterSpec`. */
class EcGenParameterSpec extends AlgorithmParameterSpec {
EcGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
/** The Java class `java.security.spec.RSAKeyGenParameterSpec`. */
class RsaKeyGenParameterSpec extends AlgorithmParameterSpec {
RsaKeyGenParameterSpec() { this.hasQualifiedName("java.security.spec", "RSAKeyGenParameterSpec") }
}
/** The Java class `java.security.spec.DSAGenParameterSpec`. */
class DsaGenParameterSpec extends AlgorithmParameterSpec {
DsaGenParameterSpec() { this.hasQualifiedName("java.security.spec", "DSAGenParameterSpec") }
}
/** The Java class `javax.crypto.spec.DHGenParameterSpec`. */
class DhGenParameterSpec extends AlgorithmParameterSpec {
DhGenParameterSpec() { this.hasQualifiedName("javax.crypto.spec", "DHGenParameterSpec") }
}

View File

@@ -0,0 +1,193 @@
/** Provides classes and predicates related to insufficient key sizes in Java. */
private import semmle.code.java.security.Encryption
private import semmle.code.java.dataflow.DataFlow
/** A source for an insufficient key size. */
abstract class InsufficientKeySizeSource extends DataFlow::Node {
/** Holds if this source has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
/** A sink for an insufficient key size. */
abstract class InsufficientKeySizeSink extends DataFlow::Node {
/** Holds if this sink has the specified `state`. */
predicate hasState(DataFlow::FlowState state) { state instanceof DataFlow::FlowStateEmpty }
}
/** Provides models for asymmetric cryptography. */
private module Asymmetric {
/** Provides models for non-elliptic-curve asymmetric cryptography. */
private module NonEllipticCurve {
/** A source for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class Source extends InsufficientKeySizeSource {
Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize() }
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in RSA, DSA, and DH algorithms. */
private class Sink extends InsufficientKeySizeSink {
Sink() {
exists(KeyPairGenInit kpgInit, KeyPairGen kpg |
kpg.getAlgoName().matches(["RSA", "DSA", "DH"]) and
DataFlow::localExprFlow(kpg, kpgInit.getQualifier()) and
this.asExpr() = kpgInit.getKeySizeArg()
)
or
exists(Spec spec | this.asExpr() = spec.getKeySizeArg())
}
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for RSA, DSA, and DH algorithms. */
private int getMinKeySize() { result = 2048 }
/** An instance of an RSA, DSA, or DH algorithm specification. */
private class Spec extends ClassInstanceExpr {
Spec() {
this.getConstructedType() instanceof RsaKeyGenParameterSpec or
this.getConstructedType() instanceof DsaGenParameterSpec or
this.getConstructedType() instanceof DhGenParameterSpec
}
/** Gets the `keysize` argument of this instance. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
}
/** Provides models for elliptic-curve asymmetric cryptography. */
private module EllipticCurve {
/** A source for an insufficient key size used in elliptic curve (EC) algorithms. */
private class Source extends InsufficientKeySizeSource {
Source() {
this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize()
or
// the below is needed for cases when the key size is embedded in the curve name
getKeySize(this.asExpr().(StringLiteral).getValue()) < getMinKeySize()
}
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in elliptic curve (EC) algorithms. */
private class Sink extends InsufficientKeySizeSink {
Sink() {
exists(KeyPairGenInit kpgInit, KeyPairGen kpg |
kpg.getAlgoName().matches("EC%") and
DataFlow::localExprFlow(kpg, kpgInit.getQualifier()) and
this.asExpr() = kpgInit.getKeySizeArg()
)
or
exists(Spec s | this.asExpr() = s.getKeySizeArg())
}
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for elliptic curve (EC) algorithms. */
private int getMinKeySize() { result = 256 }
/** Returns the key size from an EC algorithm's curve name string */
bindingset[algorithm]
private int getKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
algorithm.matches("X9.62%") and // specification such as "X9.62 prime192v2"
result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
or
algorithm.matches(["prime%", "c2tnb%"]) and // specification such as "prime192v2"
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
/** An instance of an elliptic curve (EC) algorithm specification. */
private class Spec extends ClassInstanceExpr {
Spec() { this.getConstructedType() instanceof EcGenParameterSpec }
/** Gets the `keysize` argument of this instance. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
}
/**
* A call to the `initialize` method declared in `java.security.KeyPairGenerator`
* or to the `init` method declared in `java.security.AlgorithmParameterGenerator`.
*/
private class KeyPairGenInit extends MethodAccess {
KeyPairGenInit() {
this.getMethod() instanceof KeyPairGeneratorInitMethod or
this.getMethod() instanceof AlgoParamGeneratorInitMethod
}
/** Gets the `keysize` argument of this call. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
/**
* An instance of a `java.security.KeyPairGenerator`
* or of a `java.security.AlgorithmParameterGenerator`.
*/
private class KeyPairGen extends GeneratorAlgoSpec {
KeyPairGen() {
this instanceof JavaSecurityKeyPairGenerator or
this instanceof JavaSecurityAlgoParamGenerator
}
override Expr getAlgoSpec() {
result =
[
this.(JavaSecurityKeyPairGenerator).getAlgoSpec(),
this.(JavaSecurityAlgoParamGenerator).getAlgoSpec()
]
}
}
}
/** Provides models for symmetric cryptography. */
private module Symmetric {
/** A source for an insufficient key size used in AES algorithms. */
private class Source extends InsufficientKeySizeSource {
Source() { this.asExpr().(IntegerLiteral).getIntValue() < getMinKeySize() }
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** A sink for an insufficient key size used in AES algorithms. */
private class Sink extends InsufficientKeySizeSink {
Sink() {
exists(KeyGenInit kgInit, KeyGen kg |
kg.getAlgoName() = "AES" and
DataFlow::localExprFlow(kg, kgInit.getQualifier()) and
this.asExpr() = kgInit.getKeySizeArg()
)
}
override predicate hasState(DataFlow::FlowState state) { state = getMinKeySize().toString() }
}
/** Returns the minimum recommended key size for AES algorithms. */
private int getMinKeySize() { result = 128 }
/** A call to the `init` method declared in `javax.crypto.KeyGenerator`. */
private class KeyGenInit extends MethodAccess {
KeyGenInit() { this.getMethod() instanceof KeyGeneratorInitMethod }
/** Gets the `keysize` argument of this call. */
Argument getKeySizeArg() { result = this.getArgument(0) }
}
/** An instance of a `javax.crypto.KeyGenerator`. */
private class KeyGen extends GeneratorAlgoSpec instanceof JavaxCryptoKeyGenerator {
override Expr getAlgoSpec() { result = JavaxCryptoKeyGenerator.super.getAlgoSpec() }
}
}
/** An instance of a generator that specifies an encryption algorithm. */
abstract private class GeneratorAlgoSpec extends CryptoAlgoSpec {
/** Returns an uppercase string representing the algorithm name specified by this generator object. */
string getAlgoName() {
result = this.getAlgoSpec().(CompileTimeConstantExpr).getStringValue().toUpperCase()
}
}

View File

@@ -0,0 +1,17 @@
/** Provides data flow configurations to be used in queries related to insufficient key sizes. */
import semmle.code.java.dataflow.DataFlow
import semmle.code.java.security.InsufficientKeySize
/** A data flow configuration for tracking key sizes used in cryptographic algorithms. */
class KeySizeConfiguration extends DataFlow::Configuration {
KeySizeConfiguration() { this = "KeySizeConfiguration" }
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source.(InsufficientKeySizeSource).hasState(state)
}
override predicate isSink(DataFlow::Node sink, DataFlow::FlowState state) {
sink.(InsufficientKeySizeSink).hasState(state)
}
}

View File

@@ -0,0 +1,55 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>Modern encryption relies on the computational infeasibility of breaking a cipher and decoding its
message without the key. As computational power increases, the ability to break ciphers grows, and key
sizes need to become larger as a result. Cryptographic algorithms that use too small of a key size are
vulnerable to brute force attacks, which can reveal sensitive data.</p>
</overview>
<recommendation>
<p>Use a key of the recommended size or larger. The key size should be at least 128 bits for AES encryption,
256 bits for elliptic-curve cryptography (ECC), and 2048 bits for RSA, DSA, or DH encryption.</p>
</recommendation>
<example>
<p>
The following code uses cryptographic algorithms with insufficient key sizes.
</p>
<sample src="InsufficientKeySizeBad.java" />
<p>
To fix the code, change the key sizes to be the recommended size or
larger for each algorithm.
</p>
</example>
<references>
<li>
Wikipedia:
<a href="http://en.wikipedia.org/wiki/Key_size">Key size</a>.
</li>
<li>
Wikipedia: <a href="https://en.wikipedia.org/wiki/Strong_cryptography">Strong cryptography</a>.
</li>
<li>
OWASP: <a href="https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html#algorithms">
Cryptographic Storage Cheat Sheet</a>.
</li>
<li>
OWASP: <a href="https://owasp.org/www-project-web-security-testing-guide/stable/4-Web_Application_Security_Testing/09-Testing_for_Weak_Cryptography/04-Testing_for_Weak_Encryption">
Testing for Weak Encryption</a>.
</li>
<li>
NIST:
<a href="https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-131Ar2.pdf">
Transitioning the Use of Cryptographic Algorithms and Key Lengths</a>.
</li>
</references>
</qhelp>

View File

@@ -0,0 +1,22 @@
/**
* @name Use of a cryptographic algorithm with insufficient key size
* @description Using cryptographic algorithms with too small a key size can
* allow an attacker to compromise security.
* @kind path-problem
* @problem.severity warning
* @security-severity 7.5
* @precision high
* @id java/insufficient-key-size
* @tags security
* external/cwe/cwe-326
*/
import java
import semmle.code.java.security.InsufficientKeySizeQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, KeySizeConfiguration cfg
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink,
"This $@ is less than the recommended key size of " + source.getState() + " bits.",
source.getNode(), "key size"

View File

@@ -0,0 +1,15 @@
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
keyPairGen1.initialize(1024); // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
keyPairGen2.initialize(1024); // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
keyPairGen3.initialize(1024); // BAD: Key size is less than 2048
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // BAD: Key size is less than 256
keyPairGen4.initialize(ecSpec);
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(64); // BAD: Key size is less than 128

View File

@@ -0,0 +1,4 @@
---
category: newQuery
---
* The query `java/insufficient-key-size` has been promoted from experimental to the main query pack. Its results will now appear by default. This query was originally [submitted as an experimental query by @luchua-bc](https://github.com/github/codeql/pull/4926).

View File

@@ -1,37 +0,0 @@
public class InsufficientKeySize {
public void CryptoMethod() {
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
// BAD: Key size is less than 128
keyGen1.init(64);
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
// GOOD: Key size is no less than 128
keyGen2.init(128);
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
// BAD: Key size is less than 2048
keyPairGen1.initialize(1024);
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
// GOOD: Key size is no less than 2048
keyPairGen2.initialize(2048);
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
// BAD: Key size is less than 2048
keyPairGen3.initialize(1024);
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
// GOOD: Key size is no less than 2048
keyPairGen4.initialize(2048);
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
keyPairGen5.initialize(ecSpec1);
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
// GOOD: Key size is no less than 256
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
keyPairGen6.initialize(ecSpec2);
}
}

View File

@@ -1,29 +0,0 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p>This rule finds uses of encryption algorithms with too small a key size. Encryption algorithms
are vulnerable to brute force attack when too small a key size is used.</p>
</overview>
<recommendation>
<p>The key should be at least 2048 bits long when using RSA and DSA encryption, 256 bits long when using EC encryption, and 128 bits long when using
symmetric encryption.</p>
</recommendation>
<references>
<li>
Wikipedia.
<a href="http://en.wikipedia.org/wiki/Key_size">Key size</a>
</li>
<li>
SonarSource.
<a href="https://rules.sonarsource.com/java/type/Vulnerability/RSPEC-4426">Cryptographic keys should be robust</a>
</li>
<li>
CWE.
<a href="https://cwe.mitre.org/data/definitions/326.html">CWE-326: Inadequate Encryption Strength</a>
</li>
</references>
</qhelp>

View File

@@ -1,153 +0,0 @@
/**
* @name Weak encryption: Insufficient key size
* @description Finds uses of encryption algorithms with too small a key size
* @kind problem
* @problem.severity warning
* @precision medium
* @id java/insufficient-key-size
* @tags security
* external/cwe/cwe-326
*/
import java
import semmle.code.java.security.Encryption
import semmle.code.java.dataflow.TaintTracking
/** The Java class `java.security.spec.ECGenParameterSpec`. */
class ECGenParameterSpec extends RefType {
ECGenParameterSpec() { this.hasQualifiedName("java.security.spec", "ECGenParameterSpec") }
}
/** The `init` method declared in `javax.crypto.KeyGenerator`. */
class KeyGeneratorInitMethod extends Method {
KeyGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyGenerator and
this.hasName("init")
}
}
/** The `initialize` method declared in `java.security.KeyPairGenerator`. */
class KeyPairGeneratorInitMethod extends Method {
KeyPairGeneratorInitMethod() {
this.getDeclaringType() instanceof KeyPairGenerator and
this.hasName("initialize")
}
}
/** Returns the key size in the EC algorithm string */
bindingset[algorithm]
int getECKeySize(string algorithm) {
algorithm.matches("sec%") and // specification such as "secp256r1"
result = algorithm.regexpCapture("sec[p|t](\\d+)[a-zA-Z].*", 1).toInt()
or
algorithm.matches("X9.62%") and //specification such as "X9.62 prime192v2"
result = algorithm.regexpCapture("X9\\.62 .*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
or
(algorithm.matches("prime%") or algorithm.matches("c2tnb%")) and //specification such as "prime192v2"
result = algorithm.regexpCapture(".*[a-zA-Z](\\d+)[a-zA-Z].*", 1).toInt()
}
/** Taint configuration tracking flow from a key generator to a `init` method call. */
class KeyGeneratorInitConfiguration extends TaintTracking::Configuration {
KeyGeneratorInitConfiguration() { this = "KeyGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof JavaxCryptoKeyGenerator
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
ma.getMethod() instanceof KeyGeneratorInitMethod and
sink.asExpr() = ma.getQualifier()
)
}
}
/** Taint configuration tracking flow from a keypair generator to a `initialize` method call. */
class KeyPairGeneratorInitConfiguration extends TaintTracking::Configuration {
KeyPairGeneratorInitConfiguration() { this = "KeyPairGeneratorInitConfiguration" }
override predicate isSource(DataFlow::Node source) {
source.asExpr() instanceof JavaSecurityKeyPairGenerator
}
override predicate isSink(DataFlow::Node sink) {
exists(MethodAccess ma |
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
sink.asExpr() = ma.getQualifier()
)
}
}
/** Holds if a symmetric `KeyGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
bindingset[type]
predicate hasShortSymmetricKey(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyGeneratorInitMethod and
exists(
JavaxCryptoKeyGenerator jcg, KeyGeneratorInitConfiguration cc, DataFlow::PathNode source,
DataFlow::PathNode dest
|
jcg.getAlgoSpec().(StringLiteral).getValue() = type and
source.getNode().asExpr() = jcg and
dest.getNode().asExpr() = ma.getQualifier() and
cc.hasFlowPath(source, dest)
) and
ma.getArgument(0).(IntegerLiteral).getIntValue() < 128 and
msg = "Key size should be at least 128 bits for " + type + " encryption."
}
/** Holds if an AES `KeyGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
predicate hasShortAESKey(MethodAccess ma, string msg) { hasShortSymmetricKey(ma, msg, "AES") }
/** Holds if an asymmetric `KeyPairGenerator` implementing encryption algorithm `type` and initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
bindingset[type]
predicate hasShortAsymmetricKeyPair(MethodAccess ma, string msg, string type) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
DataFlow::PathNode source, DataFlow::PathNode dest
|
jpg.getAlgoSpec().(StringLiteral).getValue().toUpperCase() = type and
source.getNode().asExpr() = jpg and
dest.getNode().asExpr() = ma.getQualifier() and
kc.hasFlowPath(source, dest)
) and
ma.getArgument(0).(IntegerLiteral).getIntValue() < 2048 and
msg = "Key size should be at least 2048 bits for " + type + " encryption."
}
/** Holds if a DSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
predicate hasShortDsaKeyPair(MethodAccess ma, string msg) {
hasShortAsymmetricKeyPair(ma, msg, "DSA") or hasShortAsymmetricKeyPair(ma, msg, "DH")
}
/** Holds if a RSA `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
predicate hasShortRsaKeyPair(MethodAccess ma, string msg) {
hasShortAsymmetricKeyPair(ma, msg, "RSA")
}
/** Holds if an EC `KeyPairGenerator` initialized by `ma` uses an insufficient key size. `msg` provides a human-readable description of the problem. */
predicate hasShortECKeyPair(MethodAccess ma, string msg) {
ma.getMethod() instanceof KeyPairGeneratorInitMethod and
exists(
JavaSecurityKeyPairGenerator jpg, KeyPairGeneratorInitConfiguration kc,
DataFlow::PathNode source, DataFlow::PathNode dest, ClassInstanceExpr cie
|
jpg.getAlgoSpec().(StringLiteral).getValue().matches("EC%") and // ECC variants such as ECDH and ECDSA
source.getNode().asExpr() = jpg and
dest.getNode().asExpr() = ma.getQualifier() and
kc.hasFlowPath(source, dest) and
DataFlow::localExprFlow(cie, ma.getArgument(0)) and
ma.getArgument(0).getType() instanceof ECGenParameterSpec and
getECKeySize(cie.getArgument(0).(StringLiteral).getValue()) < 256
) and
msg = "Key size should be at least 256 bits for EC encryption."
}
from Expr e, string msg
where
hasShortAESKey(e, msg) or
hasShortDsaKeyPair(e, msg) or
hasShortRsaKeyPair(e, msg) or
hasShortECKeyPair(e, msg)
select e, msg

View File

@@ -8,6 +8,10 @@
import internal.CaptureModels
class Activate extends ActiveConfiguration {
override predicate activateToSinkConfig() { any() }
}
from DataFlowTargetApi api, string sink
where sink = captureSink(api)
select sink order by sink

View File

@@ -8,6 +8,10 @@
import internal.CaptureModels
class Activate extends ActiveConfiguration {
override predicate activateFromSourceConfig() { any() }
}
from DataFlowTargetApi api, string source
where source = captureSource(api)
select source order by source

View File

@@ -5,6 +5,14 @@
private import CaptureModelsSpecific
class ActiveConfiguration extends Unit {
predicate activateThroughFlowConfig() { none() }
predicate activateFromSourceConfig() { none() }
predicate activateToSinkConfig() { none() }
}
class DataFlowTargetApi extends TargetApiSpecific {
DataFlowTargetApi() { isRelevantForDataFlowModels(this) }
}
@@ -140,7 +148,9 @@ private class TaintStore extends DataFlow::FlowState {
* This can be used to generate Flow summaries for APIs from parameter to return.
*/
private class ThroughFlowConfig extends TaintTracking::Configuration {
ThroughFlowConfig() { this = "ThroughFlowConfig" }
ThroughFlowConfig() {
this = "ThroughFlowConfig" and any(ActiveConfiguration ac).activateThroughFlowConfig()
}
override predicate isSource(DataFlow::Node source, DataFlow::FlowState state) {
source instanceof DataFlow::ParameterNode and
@@ -210,7 +220,9 @@ string captureThroughFlow(DataFlowTargetApi api) {
* via its return (then the API itself becomes a source).
*/
private class FromSourceConfiguration extends TaintTracking::Configuration {
FromSourceConfiguration() { this = "FromSourceConfiguration" }
FromSourceConfiguration() {
this = "FromSourceConfiguration" and any(ActiveConfiguration ac).activateFromSourceConfig()
}
override predicate isSource(DataFlow::Node source) { ExternalFlow::sourceNode(source, _) }
@@ -250,8 +262,13 @@ string captureSource(DataFlowTargetApi api) {
* This can be used to generate Sink summaries for APIs, if the API propagates a parameter (or enclosing type field)
* into an existing known sink (then the API itself becomes a sink).
*/
private class PropagateToSinkConfiguration extends PropagateToSinkConfigurationSpecific {
PropagateToSinkConfiguration() { this = "parameters or fields flowing into sinks" }
private class PropagateToSinkConfiguration extends TaintTracking::Configuration {
PropagateToSinkConfiguration() {
this = "parameters or fields flowing into sinks" and
any(ActiveConfiguration ac).activateToSinkConfig()
}
override predicate isSource(DataFlow::Node source) { apiSource(source) }
override predicate isSink(DataFlow::Node sink) { ExternalFlow::sinkNode(sink, _) }

View File

@@ -18,6 +18,8 @@ module TaintTracking = Tt::TaintTracking;
class Type = J::Type;
class Unit = J::Unit;
private J::Method superImpl(J::Method m) {
result = m.getAnOverride() and
not exists(result.getAnOverride()) and
@@ -223,24 +225,21 @@ predicate isOwnInstanceAccessNode(ReturnNode node) {
}
/**
* Language specific parts of the `PropagateToSinkConfiguration`.
* Holds if `source` is an api entrypoint relevant for creating sink models.
*/
class PropagateToSinkConfigurationSpecific extends TaintTracking::Configuration {
PropagateToSinkConfigurationSpecific() { this = "parameters or fields flowing into sinks" }
override predicate isSource(DataFlow::Node source) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
source.getEnclosingCallable().isPublic() and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
) and
isRelevantForModels(source.getEnclosingCallable())
}
predicate apiSource(DataFlow::Node source) {
(
source.asExpr().(J::FieldAccess).isOwnFieldAccess() or
source instanceof DataFlow::ParameterNode
) and
source.getEnclosingCallable().isPublic() and
exists(J::RefType t |
t = source.getEnclosingCallable().getDeclaringType().getAnAncestor() and
not t instanceof J::TypeObject and
t.isPublic()
) and
isRelevantForModels(source.getEnclosingCallable()) and
exists(asPartialModel(source.getEnclosingCallable()))
}
/**

View File

@@ -1,5 +1,9 @@
private import CaptureModels
private class Activate extends ActiveConfiguration {
override predicate activateThroughFlowConfig() { any() }
}
/**
* Capture fluent APIs that return `this`.
* Example of a fluent API:

View File

@@ -1,11 +0,0 @@
| InsufficientKeySize.java:9:9:9:24 | init(...) | Key size should be at least 128 bits for AES encryption. |
| InsufficientKeySize.java:17:9:17:36 | initialize(...) | Key size should be at least 2048 bits for RSA encryption. |
| InsufficientKeySize.java:25:9:25:36 | initialize(...) | Key size should be at least 2048 bits for DSA encryption. |
| InsufficientKeySize.java:34:9:34:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:38:9:38:67 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:48:9:48:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:53:9:53:39 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:58:9:58:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:68:9:68:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:78:9:78:40 | initialize(...) | Key size should be at least 256 bits for EC encryption. |
| InsufficientKeySize.java:87:9:87:37 | initialize(...) | Key size should be at least 2048 bits for DH encryption. |

View File

@@ -1,93 +0,0 @@
import java.security.KeyPairGenerator;
import java.security.spec.ECGenParameterSpec;
import javax.crypto.KeyGenerator;
public class InsufficientKeySize {
public void CryptoMethod() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
// BAD: Key size is less than 128
keyGen1.init(64);
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
// GOOD: Key size is no less than 128
keyGen2.init(128);
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
// BAD: Key size is less than 2048
keyPairGen1.initialize(1024);
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
// GOOD: Key size is no less than 2048
keyPairGen2.initialize(2048);
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
// BAD: Key size is less than 2048
keyPairGen3.initialize(1024);
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
// GOOD: Key size is no less than 2048
keyPairGen4.initialize(2048);
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1");
keyPairGen5.initialize(ecSpec1);
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
keyPairGen6.initialize(new ECGenParameterSpec("secp112r1"));
KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
// GOOD: Key size is no less than 256
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1");
keyPairGen7.initialize(ecSpec2);
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2");
keyPairGen8.initialize(ecSpec3);
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3");
keyPairGen9.initialize(ecSpec4);
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1");
keyPairGen10.initialize(ecSpec5);
KeyPairGenerator keyPairGen11 = KeyPairGenerator.getInstance("EC");
// GOOD: Key size is no less than 256
ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1");
keyPairGen11.initialize(ecSpec6);
KeyPairGenerator keyPairGen12 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2");
keyPairGen12.initialize(ecSpec7);
KeyPairGenerator keyPairGen13 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is no less than 256
ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1");
keyPairGen13.initialize(ecSpec8);
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is less than 256
ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1");
keyPairGen14.initialize(ecSpec9);
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
// BAD: Key size is no less than 256
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
keyPairGen15.initialize(ecSpec10);
KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("dh");
// BAD: Key size is less than 2048
keyPairGen16.initialize(1024);
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("DH");
// GOOD: Key size is no less than 2048
keyPairGen17.initialize(2048);
}
}

View File

@@ -1 +0,0 @@
experimental/Security/CWE/CWE-326/InsufficientKeySize.ql

View File

@@ -0,0 +1,256 @@
import javax.crypto.KeyGenerator;
import java.security.KeyPairGenerator;
import java.security.AlgorithmParameterGenerator;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.security.spec.DSAGenParameterSpec;
import javax.crypto.spec.DHGenParameterSpec;
public class InsufficientKeySizeTest {
public void keySizeTesting() throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
/* AES (Symmetric): minimum recommended key size is 128 */
{
/* Test with keysize as int */
KeyGenerator keyGen1 = KeyGenerator.getInstance("AES");
keyGen1.init(64); // $ hasInsufficientKeySize
KeyGenerator keyGen2 = KeyGenerator.getInstance("AES");
keyGen2.init(128); // Safe: Key size is no less than 128
/* Test with local variable as keysize */
final int size1 = 64; // compile-time constant
int size2 = 64; // not a compile-time constant
KeyGenerator keyGen3 = KeyGenerator.getInstance("AES");
keyGen3.init(size1); // $ hasInsufficientKeySize
KeyGenerator keyGen4 = KeyGenerator.getInstance("AES");
keyGen4.init(size2); // $ hasInsufficientKeySize
/* Test variables passed to another method */
KeyGenerator keyGen5 = KeyGenerator.getInstance("AES"); // MISSING: test KeyGenerator variable as argument
testSymmetricVariable(size2, keyGen5); // test with variable as key size
testSymmetricInt(64); // test with int literal as key size
/* Test with variable as algo name argument in `getInstance` method. */
final String algoName1 = "AES"; // compile-time constant
KeyGenerator keyGen6 = KeyGenerator.getInstance(algoName1);
keyGen6.init(64); // $ hasInsufficientKeySize
String algoName2 = "AES"; // not a compile-time constant
KeyGenerator keyGen7 = KeyGenerator.getInstance(algoName2);
keyGen7.init(64); // $ MISSING: hasInsufficientKeySize
}
// RSA (Asymmetric): minimum recommended key size is 2048
{
/* Test with keysize as int */
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("RSA");
keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("RSA");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
/* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("RSA");
RSAKeyGenParameterSpec rsaSpec = new RSAKeyGenParameterSpec(1024, null); // $ hasInsufficientKeySize
keyPairGen3.initialize(rsaSpec);
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("RSA");
keyPairGen4.initialize(new RSAKeyGenParameterSpec(1024, null)); // $ hasInsufficientKeySize
/* Test with local variable as keysize */
final int size1 = 1024; // compile-time constant
int size2 = 1024; // not a compile-time constant
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("RSA");
keyPairGen5.initialize(size1); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("RSA");
keyPairGen6.initialize(size2); // $ hasInsufficientKeySize
/* Test variables passed to another method */
KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("RSA"); // MISSING: test KeyGenerator variable as argument
testAsymmetricNonEcVariable(size2, keyPairGen7); // test with variable as key size
testAsymmetricNonEcInt(1024); // test with int literal as key size
/* Test getting key size as return value of another method */
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("RSA");
keyPairGen8.initialize(getRSAKeySize()); // $ hasInsufficientKeySize
/* Test with variable as algo name argument in `getInstance` method. */
final String algoName1 = "RSA"; // compile-time constant
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance(algoName1);
keyPairGen9.initialize(1024); // $ hasInsufficientKeySize
String algoName2 = "RSA"; // not a compile-time constant
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance(algoName2);
keyPairGen10.initialize(1024); // $ MISSING: hasInsufficientKeySize
}
// DSA (Asymmetric): minimum recommended key size is 2048
{
/* Test with keysize as int */
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("DSA");
keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DSA");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
/* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DSA");
DSAGenParameterSpec dsaSpec = new DSAGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
keyPairGen3.initialize(dsaSpec);
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DSA");
keyPairGen4.initialize(new DSAGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
/* Test `AlgorithmParameterGenerator` */
AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DSA");
paramGen.init(1024); // $ hasInsufficientKeySize
/* Test with variable as algo name argument in `getInstance` method. */
final String algoName1 = "DSA"; // compile-time constant
AlgorithmParameterGenerator paramGen1 = AlgorithmParameterGenerator.getInstance(algoName1);
paramGen1.init(1024); // $ hasInsufficientKeySize
String algoName2 = "DSA"; // not a compile-time constant
AlgorithmParameterGenerator paramGen2 = AlgorithmParameterGenerator.getInstance(algoName2);
paramGen2.init(1024); // $ MISSING: hasInsufficientKeySize
}
// DH (Asymmetric): minimum recommended key size is 2048
{
/* Test with keysize as int */
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("dh");
keyPairGen1.initialize(1024); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("DH");
keyPairGen2.initialize(2048); // Safe: Key size is no less than 2048
/* Test spec */
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("DH");
DHGenParameterSpec dhSpec = new DHGenParameterSpec(1024, 0); // $ hasInsufficientKeySize
keyPairGen3.initialize(dhSpec);
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("DH");
keyPairGen4.initialize(new DHGenParameterSpec(1024, 0)); // $ hasInsufficientKeySize
/* Test `AlgorithmParameterGenerator` */
AlgorithmParameterGenerator paramGen = AlgorithmParameterGenerator.getInstance("DH");
paramGen.init(1024); // $ hasInsufficientKeySize
}
// EC (Asymmetric): minimum recommended key size is 256
{
/* Test with keysize as int */
KeyPairGenerator keyPairGen1 = KeyPairGenerator.getInstance("EC");
keyPairGen1.initialize(128); // $ hasInsufficientKeySize
/* Test with keysize as curve name in spec */
KeyPairGenerator keyPairGen2 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec1 = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
keyPairGen2.initialize(ecSpec1);
KeyPairGenerator keyPairGen3 = KeyPairGenerator.getInstance("EC");
keyPairGen3.initialize(new ECGenParameterSpec("secp112r1")); // $ hasInsufficientKeySize
KeyPairGenerator keyPairGen4 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec2 = new ECGenParameterSpec("secp256r1"); // Safe: Key size is no less than 256
keyPairGen4.initialize(ecSpec2);
KeyPairGenerator keyPairGen5 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec3 = new ECGenParameterSpec("X9.62 prime192v2"); // $ hasInsufficientKeySize
keyPairGen5.initialize(ecSpec3);
KeyPairGenerator keyPairGen6 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec4 = new ECGenParameterSpec("X9.62 c2tnb191v3"); // $ hasInsufficientKeySize
keyPairGen6.initialize(ecSpec4);
KeyPairGenerator keyPairGen7 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec5 = new ECGenParameterSpec("sect163k1"); // $ hasInsufficientKeySize
keyPairGen7.initialize(ecSpec5);
KeyPairGenerator keyPairGen8 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec6 = new ECGenParameterSpec("X9.62 c2tnb359v1"); // Safe: Key size is no less than 256
keyPairGen8.initialize(ecSpec6);
KeyPairGenerator keyPairGen9 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec7 = new ECGenParameterSpec("prime192v2"); // $ hasInsufficientKeySize
keyPairGen9.initialize(ecSpec7);
KeyPairGenerator keyPairGen10 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec8 = new ECGenParameterSpec("prime256v1"); // Safe: Key size is no less than 256
keyPairGen10.initialize(ecSpec8);
KeyPairGenerator keyPairGen14 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec9 = new ECGenParameterSpec("c2tnb191v1"); // $ hasInsufficientKeySize
keyPairGen14.initialize(ecSpec9);
KeyPairGenerator keyPairGen15 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec10 = new ECGenParameterSpec("c2tnb431r1");
keyPairGen15.initialize(ecSpec10); // Safe: Key size is no less than 256
/* Test variables passed to another method */
ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp112r1"); // $ hasInsufficientKeySize
testAsymmetricEcSpecVariable(ecSpec); // test spec as an argument
int size = 128;
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC"); // MISSING: test KeyGenerator variable as argument
testAsymmetricEcIntVariable(size, keyPairGen); // test with variable as key size
testAsymmetricEcIntLiteral(128); // test with int literal as key size
/* Test with variable as curve name argument in `ECGenParameterSpec` constructor. */
final String curveName1 = "secp112r1"; // compile-time constant
KeyPairGenerator keyPairGen16 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec11 = new ECGenParameterSpec(curveName1); // $ hasInsufficientKeySize
keyPairGen16.initialize(ecSpec11);
String curveName2 = "secp112r1"; // not a compile-time constant
KeyPairGenerator keyPairGen17 = KeyPairGenerator.getInstance("EC");
ECGenParameterSpec ecSpec12 = new ECGenParameterSpec(curveName2); // $ hasInsufficientKeySize
keyPairGen17.initialize(ecSpec12);
}
}
public static void testSymmetricVariable(int keySize, KeyGenerator kg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keySize); // $ hasInsufficientKeySize
kg.init(64); // $ MISSING: hasInsufficientKeySize
}
public static void testSymmetricInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(keySize); // $ hasInsufficientKeySize
}
public static void testAsymmetricNonEcVariable(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
kpg.initialize(1024); // $ MISSING: hasInsufficientKeySize
}
public static void testAsymmetricNonEcInt(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
}
public static void testAsymmetricEcSpecVariable(ECGenParameterSpec spec) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(spec); // sink is above where `spec` variable is initialized
}
public static void testAsymmetricEcIntVariable(int keySize, KeyPairGenerator kpg) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
kpg.initialize(128); // $ MISSING: hasInsufficientKeySize
}
public static void testAsymmetricEcIntLiteral(int keySize) throws java.security.NoSuchAlgorithmException, java.security.InvalidAlgorithmParameterException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("EC");
keyPairGen.initialize(keySize); // $ hasInsufficientKeySize
}
public int getRSAKeySize(){ return 1024; }
}

View File

@@ -0,0 +1,20 @@
import java
import TestUtilities.InlineExpectationsTest
import semmle.code.java.security.InsufficientKeySizeQuery
class InsufficientKeySizeTest extends InlineExpectationsTest {
InsufficientKeySizeTest() { this = "InsufficientKeySize" }
override string getARelevantTag() { result = "hasInsufficientKeySize" }
override predicate hasActualResult(Location location, string element, string tag, string value) {
tag = "hasInsufficientKeySize" and
exists(DataFlow::PathNode source, DataFlow::PathNode sink |
exists(KeySizeConfiguration cfg | cfg.hasFlowPath(source, sink))
|
sink.getNode().getLocation() = location and
element = sink.getNode().toString() and
value = ""
)
}
}

View File

@@ -0,0 +1,31 @@
/* Adds tests to check for FPs related to RSA/DSA versus EC */
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.Security;
import java.security.Signature;
public class SignatureTest
{
public void performTest()
throws Exception
{
KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
kpGen.initialize(2048); // Safe
KeyPair kp = kpGen.generateKeyPair();
kpGen = KeyPairGenerator.getInstance("DSA", "BC");
kpGen.initialize(2048); // Safe
kp = kpGen.generateKeyPair();
kpGen = KeyPairGenerator.getInstance("EC", "BC");
kpGen.initialize(256); // Safe
kp = kpGen.generateKeyPair();
kpGen = KeyPairGenerator.getInstance("EC", "BC");
kpGen.initialize(521); // Safe
kp = kpGen.generateKeyPair();
}
}

View File

@@ -41,7 +41,7 @@ public class Main {
* A version identifier that should be updated every time the extractor changes in such a way that
* it may produce different tuples for the same file under the same {@link ExtractorConfig}.
*/
public static final String EXTRACTOR_VERSION = "2022-09-19";
public static final String EXTRACTOR_VERSION = "2022-11-08";
public static final Pattern NEWLINE = Pattern.compile("\n");

View File

@@ -3,6 +3,7 @@ package com.semmle.js.extractor;
import com.semmle.util.data.StringUtil;
import com.semmle.util.exception.CatastrophicError;
import com.semmle.util.exception.UserError;
import com.semmle.util.locations.LineTable;
import com.semmle.util.trap.TrapWriter;
import com.semmle.util.trap.TrapWriter.Label;
import com.semmle.util.trap.TrapWriter.Table;
@@ -76,8 +77,10 @@ public class YAMLExtractor implements IExtractor {
private final boolean tolerateParseErrors;
private TextualExtractor textualExtractor;
private LocationManager locationManager;
private TrapWriter trapWriter;
private LineTable lineTable;
/**
* The underlying SnakeYAML parser; we use the relatively low-level {@linkplain Parser} instead of
@@ -93,8 +96,16 @@ public class YAMLExtractor implements IExtractor {
this.tolerateParseErrors = config.isTolerateParseErrors();
}
private LineTable getLineTable() {
if (lineTable == null) {
lineTable = new LineTable(this.textualExtractor.getSource());
}
return lineTable;
}
@Override
public LoCInfo extract(TextualExtractor textualExtractor) {
this.textualExtractor = textualExtractor;
locationManager = textualExtractor.getLocationManager();
trapWriter = textualExtractor.getTrapwriter();
@@ -253,6 +264,18 @@ public class YAMLExtractor implements IExtractor {
endLine = endMark.getLine() + 1;
endColumn = endMark.getColumn();
// Avoid emitting column zero for non-empty locations
if (endColumn == 0 && !(startLine == endLine && startColumn == endColumn)) {
String source = textualExtractor.getSource();
int offset = getLineTable().getOffsetFromPoint(endMark.getLine(), endMark.getColumn()) - 1;
while (offset > 0 && isNewLine((int)source.charAt(offset))) {
--offset;
}
com.semmle.util.locations.Position adjustedEndPos = getLineTable().getEndPositionFromOffset(offset);
endLine = adjustedEndPos.getLine();
endColumn = adjustedEndPos.getColumn();
}
locationManager.emitSnippetLocation(label, startLine, startColumn, endLine, endColumn);
}
}

View File

@@ -28,12 +28,12 @@ yaml(#20006,3,#20003,-1,"","*cyc")
locations_default(#20007,#10000,3,15,3,18)
yaml_locations(#20006,#20007)
yaml(#20003,1,#20000,-1,"tag:yaml.org,2002:map","&cyc")
#20008=@"loc,{#10000},2,9,4,0"
locations_default(#20008,#10000,2,9,4,0)
#20008=@"loc,{#10000},2,9,3,19"
locations_default(#20008,#10000,2,9,3,19)
yaml_locations(#20003,#20008)
yaml(#20000,1,#10000,0,"tag:yaml.org,2002:map","cyclic: &cyc")
#20009=@"loc,{#10000},2,1,4,0"
locations_default(#20009,#10000,2,1,4,0)
#20009=@"loc,{#10000},2,1,3,19"
locations_default(#20009,#10000,2,1,3,19)
yaml_locations(#20000,#20009)
numlines(#10000,3,0,0)
filetype(#10000,"yaml")

View File

@@ -33,8 +33,8 @@ yaml(#20008,0,#20007,0,"tag:yaml.org,2002:str","xx [[ "" ... xxx; xx")
locations_default(#20009,#10000,4,5,4,102)
yaml_locations(#20008,#20009)
yaml(#20007,2,#20000,-2,"tag:yaml.org,2002:seq","- xx [[ ... xxx; xx")
#20010=@"loc,{#10000},4,3,6,0"
locations_default(#20010,#10000,4,3,6,0)
#20010=@"loc,{#10000},4,3,4,103"
locations_default(#20010,#10000,4,3,4,103)
yaml_locations(#20007,#20010)
#20011=*
yaml_scalars(#20011,0,"xxxxxx")
@@ -88,16 +88,16 @@ yaml(#20028,0,#20017,-3,"tag:yaml.org,2002:str","xxxxxx")
locations_default(#20029,#10000,10,13,10,18)
yaml_locations(#20028,#20029)
yaml(#20017,1,#20016,0,"tag:yaml.org,2002:map","xx: xxxxx")
#20030=@"loc,{#10000},8,7,14,0"
locations_default(#20030,#10000,8,7,14,0)
#20030=@"loc,{#10000},8,7,12,27"
locations_default(#20030,#10000,8,7,12,27)
yaml_locations(#20017,#20030)
yaml(#20016,2,#20013,-1,"tag:yaml.org,2002:seq","- xx: xxxxx")
#20031=@"loc,{#10000},8,5,14,0"
locations_default(#20031,#10000,8,5,14,0)
#20031=@"loc,{#10000},8,5,12,27"
locations_default(#20031,#10000,8,5,12,27)
yaml_locations(#20016,#20031)
yaml(#20013,1,#20000,-3,"tag:yaml.org,2002:map","xxxxxxx:")
#20032=@"loc,{#10000},7,3,14,0"
locations_default(#20032,#10000,7,3,14,0)
#20032=@"loc,{#10000},7,3,12,27"
locations_default(#20032,#10000,7,3,12,27)
yaml_locations(#20013,#20032)
#20033=*
yaml_scalars(#20033,0,"xxxxx")
@@ -120,12 +120,12 @@ yaml(#20039,0,#20038,0,"tag:yaml.org,2002:str","xxxx_xxxxxxx")
locations_default(#20040,#10000,16,7,16,18)
yaml_locations(#20039,#20040)
yaml(#20038,2,#20035,-1,"tag:yaml.org,2002:seq","- xxxx_xxxxxxx")
#20041=@"loc,{#10000},16,5,18,0"
locations_default(#20041,#10000,16,5,18,0)
#20041=@"loc,{#10000},16,5,16,19"
locations_default(#20041,#10000,16,5,16,19)
yaml_locations(#20038,#20041)
yaml(#20035,1,#20000,-4,"tag:yaml.org,2002:map","xxxxxxxxxxx:")
#20042=@"loc,{#10000},15,3,18,0"
locations_default(#20042,#10000,15,3,18,0)
#20042=@"loc,{#10000},15,3,16,19"
locations_default(#20042,#10000,15,3,16,19)
yaml_locations(#20035,#20042)
#20043=*
yaml_scalars(#20043,0,"xxxxxx")
@@ -171,8 +171,8 @@ yaml(#20056,0,#20045,-3,"tag:yaml.org,2002:str","xxx xxx xxxx")
locations_default(#20057,#10000,21,11,21,22)
yaml_locations(#20056,#20057)
yaml(#20045,1,#20000,-5,"tag:yaml.org,2002:map","xxxx_xxxxxxx: xxxx")
#20058=@"loc,{#10000},19,3,23,0"
locations_default(#20058,#10000,19,3,23,0)
#20058=@"loc,{#10000},19,3,21,23"
locations_default(#20058,#10000,19,3,21,23)
yaml_locations(#20045,#20058)
#20059=*
yaml_scalars(#20059,0,"xxx")
@@ -201,16 +201,16 @@ yaml(#20067,0,#20064,-1,"tag:yaml.org,2002:str","xxxxxxx ... xxxxxx=")
locations_default(#20068,#10000,26,13,26,696)
yaml_locations(#20067,#20068)
yaml(#20064,1,#20061,-1,"tag:yaml.org,2002:map","xxxxxx: ... xxxxxx=")
#20069=@"loc,{#10000},26,5,27,0"
locations_default(#20069,#10000,26,5,27,0)
#20069=@"loc,{#10000},26,5,26,697"
locations_default(#20069,#10000,26,5,26,697)
yaml_locations(#20064,#20069)
yaml(#20061,1,#20000,-6,"tag:yaml.org,2002:map","xxxxxx:")
#20070=@"loc,{#10000},24,3,27,0"
locations_default(#20070,#10000,24,3,27,0)
#20070=@"loc,{#10000},24,3,26,697"
locations_default(#20070,#10000,24,3,26,697)
yaml_locations(#20061,#20070)
yaml(#20000,1,#10000,0,"tag:yaml.org,2002:map","xxxxxxxx: xxxx_xx")
#20071=@"loc,{#10000},1,1,27,0"
locations_default(#20071,#10000,1,1,27,0)
#20071=@"loc,{#10000},1,1,26,697"
locations_default(#20071,#10000,1,1,26,697)
yaml_locations(#20000,#20071)
numlines(#10000,26,0,0)
filetype(#10000,"yaml")

View File

@@ -63,8 +63,8 @@ yaml(#20018,0,#20011,-2,"tag:yaml.org,2002:str","Gale")
locations_default(#20019,#10000,6,14,6,17)
yaml_locations(#20018,#20019)
yaml(#20011,1,#20000,-3,"tag:yaml.org,2002:map","given: Dorothy")
#20020=@"loc,{#10000},5,5,8,0"
locations_default(#20020,#10000,5,5,8,0)
#20020=@"loc,{#10000},5,5,6,18"
locations_default(#20020,#10000,5,5,6,18)
yaml_locations(#20011,#20020)
#20021=*
yaml_scalars(#20021,0,"items")
@@ -188,12 +188,12 @@ yaml(#20061,0,#20042,-5,"tag:yaml.org,2002:int","1")
locations_default(#20062,#10000,18,18,18,18)
yaml_locations(#20061,#20062)
yaml(#20042,1,#20023,1,"tag:yaml.org,2002:map","part_no: E1628")
#20063=@"loc,{#10000},14,7,20,0"
locations_default(#20063,#10000,14,7,20,0)
#20063=@"loc,{#10000},14,7,18,19"
locations_default(#20063,#10000,14,7,18,19)
yaml_locations(#20042,#20063)
yaml(#20023,2,#20000,-4,"tag:yaml.org,2002:seq","- part_no: A4786")
#20064=@"loc,{#10000},9,5,20,0"
locations_default(#20064,#10000,9,5,20,0)
#20064=@"loc,{#10000},9,5,18,19"
locations_default(#20064,#10000,9,5,18,19)
yaml_locations(#20023,#20064)
#20065=*
yaml_scalars(#20065,0,"bill-to")
@@ -214,8 +214,8 @@ yaml_scalars(#20070,124,"123 Tornado Alley
Suite 16
")
yaml(#20070,0,#20067,-1,"tag:yaml.org,2002:str","|")
#20071=@"loc,{#10000},21,13,24,0"
locations_default(#20071,#10000,21,13,24,0)
#20071=@"loc,{#10000},21,13,23,21"
locations_default(#20071,#10000,21,13,23,21)
yaml_locations(#20070,#20071)
#20072=*
yaml_scalars(#20072,0,"city")
@@ -242,8 +242,8 @@ yaml(#20078,0,#20067,-3,"tag:yaml.org,2002:str","KS")
locations_default(#20079,#10000,25,13,25,14)
yaml_locations(#20078,#20079)
yaml(#20067,1,#20000,-5,"tag:yaml.org,2002:map","&id001")
#20080=@"loc,{#10000},20,11,27,0"
locations_default(#20080,#10000,20,11,27,0)
#20080=@"loc,{#10000},20,11,25,15"
locations_default(#20080,#10000,20,11,25,15)
yaml_locations(#20067,#20080)
#20081=*
yaml_scalars(#20081,0,"ship-to")

View File

@@ -75,6 +75,16 @@ private DataFlow::Node getAValueExportedByPackage() {
result = getAnExportFromModule(mod)
)
or
// re-export of a value from another module
// `module.exports.foo = require("./other").bar;`
// other.js:
// `module.exports.bar = function () { ... };`
exists(DataFlow::PropRead read, Import imp |
read = getAValueExportedByPackage() and
read.getBase().getALocalSource() = imp.getImportedModuleNode() and
result = imp.getImportedModule().getAnExportedValue(read.getPropertyName())
)
or
// require("./other-module.js"); inside an AMD module.
exists(Module mod, CallExpr call |
call = getAValueExportedByPackage().asExpr() and

View File

@@ -0,0 +1,5 @@
---
category: fix
---
* Fixed an issue with multi-line strings in YAML files being associated with an invalid location,
causing alerts related to such strings to appear at the top of the YAML file.

View File

@@ -2,10 +2,10 @@
| sub/.eslintrc.json:2:14:5:3 | {\\n ... lse\\n } | aNonWritableGlobal | false | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.json:2:14:5:3 | {\\n ... lse\\n } | aWritableGlobal | true | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.json:2:14:5:3 | {\\n ... lse\\n } | aWritableGlobal | true | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:6:0 | aWritab ... l: true | aNonWritableGlobal | false | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:6:0 | aWritab ... l: true | aNonWritableGlobal | false | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:6:0 | aWritab ... l: true | aWritableGlobal | true | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:6:0 | aWritab ... l: true | aWritableGlobal | true | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:4:30 | aWritab ... l: true | aNonWritableGlobal | false | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:4:30 | aWritab ... l: true | aNonWritableGlobal | false | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:4:30 | aWritab ... l: true | aWritableGlobal | true | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/.eslintrc.yml:3:5:4:30 | aWritab ... l: true | aWritableGlobal | true | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/package.json:5:20:8:9 | {\\n ... } | aNonWritableGlobal | false | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/package.json:5:20:8:9 | {\\n ... } | aNonWritableGlobal | false | sub/tst.js:1:1:1:15 | aWritableGlobal |
| sub/package.json:5:20:8:9 | {\\n ... } | aWritableGlobal | true | sub/subsub/tst.js:1:1:1:15 | aWritableGlobal |

View File

@@ -30,7 +30,7 @@ nodes
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | semmle.label | [YamlSequence] - "name ... Knopf" |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | semmle.order | 3 |
| tst.yml:1:3:1:8 | [YamlScalar] "name" | semmle.label | [YamlScalar] "name" |
| tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | semmle.label | [YamlMapping] "name": "Jim Knopf" |
| tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | semmle.label | [YamlMapping] "name": "Jim Knopf" |
| tst.yml:1:11:1:21 | [YamlScalar] "Jim Knopf" | semmle.label | [YamlScalar] "Jim Knopf" |
| tst.yml:2:3:2:9 | [YamlScalar] address | semmle.label | [YamlScalar] address |
| tst.yml:2:12:6:3 | [YamlMapping] { | semmle.label | [YamlMapping] { |
@@ -41,12 +41,12 @@ nodes
| tst.yml:5:5:5:13 | [YamlScalar] "country" | semmle.label | [YamlScalar] "country" |
| tst.yml:5:16:5:27 | [YamlScalar] "Lummerland" | semmle.label | [YamlScalar] "Lummerland" |
| tst.yml:7:3:7:6 | [YamlScalar] name | semmle.label | [YamlScalar] name |
| tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | semmle.label | [YamlMapping] name: Frau Mahlzahn |
| tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | semmle.label | [YamlMapping] name: Frau Mahlzahn |
| tst.yml:7:9:7:21 | [YamlScalar] Frau Mahlzahn | semmle.label | [YamlScalar] Frau Mahlzahn |
| tst.yml:8:3:8:9 | [YamlScalar] address | semmle.label | [YamlScalar] address |
| tst.yml:9:5:9:10 | [YamlScalar] street | semmle.label | [YamlScalar] street |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | semmle.label | [YamlMapping] street: \| |
| tst.yml:9:13:11:0 | [YamlScalar] \| | semmle.label | [YamlScalar] \| |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | semmle.label | [YamlMapping] street: \| |
| tst.yml:9:13:10:21 | [YamlScalar] \| | semmle.label | [YamlScalar] \| |
| tst.yml:11:5:11:10 | [YamlScalar] number | semmle.label | [YamlScalar] number |
| tst.yml:11:13:11:15 | [YamlScalar] 133 | semmle.label | [YamlScalar] 133 |
| tst.yml:12:5:12:11 | [YamlScalar] country | semmle.label | [YamlScalar] country |
@@ -67,8 +67,8 @@ edges
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:3:14:3:13 | [YamlScalar] | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:5:9:10 | [YamlScalar] street | semmle.label | 0 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:5:9:10 | [YamlScalar] street | semmle.order | 0 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:13:11:0 | [YamlScalar] \| | semmle.label | 1 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:13:11:0 | [YamlScalar] \| | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:13:10:21 | [YamlScalar] \| | semmle.label | 1 |
| file://:0:0:0:0 | (Mapping 0) street: | tst.yml:9:13:10:21 | [YamlScalar] \| | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 0) x: | merge.yaml:1:8:1:8 | [YamlScalar] x | semmle.label | 0 |
| file://:0:0:0:0 | (Mapping 0) x: | merge.yaml:1:8:1:8 | [YamlScalar] x | semmle.order | 0 |
| file://:0:0:0:0 | (Mapping 0) x: | merge.yaml:1:11:1:12 | [YamlScalar] 23 | semmle.label | 1 |
@@ -87,8 +87,8 @@ edges
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:2:12:6:3 | [YamlMapping] { | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:8:3:8:9 | [YamlScalar] address | semmle.label | 0 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:8:3:8:9 | [YamlScalar] address | semmle.order | 0 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:9:5:14:0 | [YamlMapping] street: \| | semmle.label | 1 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:9:5:14:0 | [YamlMapping] street: \| | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:9:5:13:19 | [YamlMapping] street: \| | semmle.label | 1 |
| file://:0:0:0:0 | (Mapping 1) address: | tst.yml:9:5:13:19 | [YamlMapping] street: \| | semmle.order | 1 |
| file://:0:0:0:0 | (Mapping 1) number: | tst.yml:4:5:4:12 | [YamlScalar] "number" | semmle.label | 0 |
| file://:0:0:0:0 | (Mapping 1) number: | tst.yml:4:5:4:12 | [YamlScalar] "number" | semmle.order | 0 |
| file://:0:0:0:0 | (Mapping 1) number: | tst.yml:4:15:4:16 | [YamlScalar] -1 | semmle.label | 1 |
@@ -121,31 +121,31 @@ edges
| merge.yaml:2:3:3:8 | [YamlMapping] x: 56 | file://:0:0:0:0 | (Mapping 0) x: | semmle.order | 0 |
| merge.yaml:2:3:3:8 | [YamlMapping] x: 56 | file://:0:0:0:0 | (Mapping 1) <<: | semmle.label | 1 |
| merge.yaml:2:3:3:8 | [YamlMapping] x: 56 | file://:0:0:0:0 | (Mapping 1) <<: | semmle.order | 1 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | semmle.label | 0 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | semmle.order | 0 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | semmle.label | 1 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | semmle.order | 1 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | semmle.label | 0 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | semmle.order | 0 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | semmle.label | 1 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | semmle.order | 1 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:14:3:14:23 | [YamlScalar] !includ ... nal.yml | semmle.label | 2 |
| tst.yml:1:1:14:23 | [YamlSequence] - "name ... Knopf" | tst.yml:14:3:14:23 | [YamlScalar] !includ ... nal.yml | semmle.order | 2 |
| tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 0) name: | semmle.label | 0 |
| tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 0) name: | semmle.order | 0 |
| tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 1) address: | semmle.label | 1 |
| tst.yml:1:3:7:0 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 1) address: | semmle.order | 1 |
| tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 0) name: | semmle.label | 0 |
| tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 0) name: | semmle.order | 0 |
| tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 1) address: | semmle.label | 1 |
| tst.yml:1:3:6:4 | [YamlMapping] "name": "Jim Knopf" | file://:0:0:0:0 | (Mapping 1) address: | semmle.order | 1 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 0) street: | semmle.label | 0 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 0) street: | semmle.order | 0 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 1) number: | semmle.label | 1 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 1) number: | semmle.order | 1 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 2) country: | semmle.label | 2 |
| tst.yml:2:12:6:3 | [YamlMapping] { | file://:0:0:0:0 | (Mapping 2) country: | semmle.order | 2 |
| tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 0) name: | semmle.label | 0 |
| tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 0) name: | semmle.order | 0 |
| tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 1) address: | semmle.label | 1 |
| tst.yml:7:3:14:0 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 1) address: | semmle.order | 1 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 0) street: | semmle.label | 0 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 0) street: | semmle.order | 0 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 1) number: | semmle.label | 1 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 1) number: | semmle.order | 1 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 2) country: | semmle.label | 2 |
| tst.yml:9:5:14:0 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 2) country: | semmle.order | 2 |
| tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 0) name: | semmle.label | 0 |
| tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 0) name: | semmle.order | 0 |
| tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 1) address: | semmle.label | 1 |
| tst.yml:7:3:13:19 | [YamlMapping] name: Frau Mahlzahn | file://:0:0:0:0 | (Mapping 1) address: | semmle.order | 1 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 0) street: | semmle.label | 0 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 0) street: | semmle.order | 0 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 1) number: | semmle.label | 1 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 1) number: | semmle.order | 1 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 2) country: | semmle.label | 2 |
| tst.yml:9:5:13:19 | [YamlMapping] street: \| | file://:0:0:0:0 | (Mapping 2) country: | semmle.order | 2 |
graphProperties
| semmle.graphKind | tree |

View File

@@ -12,16 +12,16 @@ yamlMapping_maps
| merge.yaml:2:3:3:8 | x: 56 | merge.yaml:1:15:1:15 | y | merge.yaml:1:18:1:19 | 42 |
| merge.yaml:2:3:3:8 | x: 56 | merge.yaml:2:3:2:3 | x | merge.yaml:2:6:2:7 | 56 |
| merge.yaml:2:3:3:8 | x: 56 | merge.yaml:3:3:3:4 | << | merge.yaml:1:3:1:21 | &A { x: 23, y: 42 } |
| tst.yml:1:3:7:0 | "name": "Jim Knopf" | tst.yml:1:3:1:8 | "name" | tst.yml:1:11:1:21 | "Jim Knopf" |
| tst.yml:1:3:7:0 | "name": "Jim Knopf" | tst.yml:2:3:2:9 | address | tst.yml:2:12:6:3 | { |
| tst.yml:1:3:6:4 | "name": "Jim Knopf" | tst.yml:1:3:1:8 | "name" | tst.yml:1:11:1:21 | "Jim Knopf" |
| tst.yml:1:3:6:4 | "name": "Jim Knopf" | tst.yml:2:3:2:9 | address | tst.yml:2:12:6:3 | { |
| tst.yml:2:12:6:3 | { | tst.yml:3:5:3:12 | "street" | tst.yml:3:14:3:13 | |
| tst.yml:2:12:6:3 | { | tst.yml:4:5:4:12 | "number" | tst.yml:4:15:4:16 | -1 |
| tst.yml:2:12:6:3 | { | tst.yml:5:5:5:13 | "country" | tst.yml:5:16:5:27 | "Lummerland" |
| tst.yml:7:3:14:0 | name: Frau Mahlzahn | tst.yml:7:3:7:6 | name | tst.yml:7:9:7:21 | Frau Mahlzahn |
| tst.yml:7:3:14:0 | name: Frau Mahlzahn | tst.yml:8:3:8:9 | address | tst.yml:9:5:14:0 | street: \| |
| tst.yml:9:5:14:0 | street: \| | tst.yml:9:5:9:10 | street | tst.yml:9:13:11:0 | \| |
| tst.yml:9:5:14:0 | street: \| | tst.yml:11:5:11:10 | number | tst.yml:11:13:11:15 | 133 |
| tst.yml:9:5:14:0 | street: \| | tst.yml:12:5:12:11 | country | tst.yml:12:14:13:18 | < |
| tst.yml:7:3:13:19 | name: Frau Mahlzahn | tst.yml:7:3:7:6 | name | tst.yml:7:9:7:21 | Frau Mahlzahn |
| tst.yml:7:3:13:19 | name: Frau Mahlzahn | tst.yml:8:3:8:9 | address | tst.yml:9:5:13:19 | street: \| |
| tst.yml:9:5:13:19 | street: \| | tst.yml:9:5:9:10 | street | tst.yml:9:13:10:21 | \| |
| tst.yml:9:5:13:19 | street: \| | tst.yml:11:5:11:10 | number | tst.yml:11:13:11:15 | 133 |
| tst.yml:9:5:13:19 | street: \| | tst.yml:12:5:12:11 | country | tst.yml:12:14:13:18 | < |
yamlNode
| external.yml:1:1:1:2 | 42 | tag:yaml.org,2002:int |
| merge.yaml:1:1:3:8 | - &A { ... y: 42 } | tag:yaml.org,2002:seq |
@@ -37,7 +37,7 @@ yamlNode
| merge.yaml:3:7:3:8 | *A | |
| tst.yml:1:1:14:23 | - "name ... Knopf" | tag:yaml.org,2002:seq |
| tst.yml:1:3:1:8 | "name" | tag:yaml.org,2002:str |
| tst.yml:1:3:7:0 | "name": "Jim Knopf" | tag:yaml.org,2002:map |
| tst.yml:1:3:6:4 | "name": "Jim Knopf" | tag:yaml.org,2002:map |
| tst.yml:1:11:1:21 | "Jim Knopf" | tag:yaml.org,2002:str |
| tst.yml:2:3:2:9 | address | tag:yaml.org,2002:str |
| tst.yml:2:12:6:3 | { | tag:yaml.org,2002:map |
@@ -48,12 +48,12 @@ yamlNode
| tst.yml:5:5:5:13 | "country" | tag:yaml.org,2002:str |
| tst.yml:5:16:5:27 | "Lummerland" | tag:yaml.org,2002:str |
| tst.yml:7:3:7:6 | name | tag:yaml.org,2002:str |
| tst.yml:7:3:14:0 | name: Frau Mahlzahn | tag:yaml.org,2002:map |
| tst.yml:7:3:13:19 | name: Frau Mahlzahn | tag:yaml.org,2002:map |
| tst.yml:7:9:7:21 | Frau Mahlzahn | tag:yaml.org,2002:str |
| tst.yml:8:3:8:9 | address | tag:yaml.org,2002:str |
| tst.yml:9:5:9:10 | street | tag:yaml.org,2002:str |
| tst.yml:9:5:14:0 | street: \| | tag:yaml.org,2002:map |
| tst.yml:9:13:11:0 | \| | tag:yaml.org,2002:str |
| tst.yml:9:5:13:19 | street: \| | tag:yaml.org,2002:map |
| tst.yml:9:13:10:21 | \| | tag:yaml.org,2002:str |
| tst.yml:11:5:11:10 | number | tag:yaml.org,2002:str |
| tst.yml:11:13:11:15 | 133 | tag:yaml.org,2002:int |
| tst.yml:12:5:12:11 | country | tag:yaml.org,2002:str |
@@ -81,7 +81,7 @@ yamlScalar
| tst.yml:7:9:7:21 | Frau Mahlzahn | | Frau Mahlzahn |
| tst.yml:8:3:8:9 | address | | address |
| tst.yml:9:5:9:10 | street | | street |
| tst.yml:9:13:11:0 | \| | \| | Alte Strasse\n |
| tst.yml:9:13:10:21 | \| | \| | Alte Strasse\n |
| tst.yml:11:5:11:10 | number | | number |
| tst.yml:11:13:11:15 | 133 | | 133 |
| tst.yml:12:5:12:11 | country | | country |

View File

@@ -2,6 +2,12 @@ on: issue_comment
jobs:
echo-chamber:
runs-on: ubuntu-latest
steps:
- run: |
echo '${{ github.event.comment.body }}'
echo-chamber2:
runs-on: ubuntu-latest
steps:
- run: |

View File

@@ -0,0 +1,10 @@
on: issue_comment
# same as comment_issue but this file ends with a line break
jobs:
echo-chamber:
runs-on: ubuntu-latest
steps:
- run: |
echo '${{ github.event.comment.body }}'

View File

@@ -1 +1,3 @@
| .github/workflows/comment_issue.yml:7:12:8:47 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
| .github/workflows/comment_issue.yml:7:12:8:48 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
| .github/workflows/comment_issue.yml:13:12:14:47 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |
| .github/workflows/comment_issue_newline.yml:9:14:10:50 | \| | Potential injection from the github.event.comment.body context, which may be controlled by an external user. |

View File

@@ -117,6 +117,12 @@ nodes
| lib.js:128:9:128:20 | obj[path[0]] |
| lib.js:128:13:128:16 | path |
| lib.js:128:13:128:19 | path[0] |
| sublib/other.js:5:28:5:31 | path |
| sublib/other.js:5:28:5:31 | path |
| sublib/other.js:6:7:6:18 | obj[path[0]] |
| sublib/other.js:6:7:6:18 | obj[path[0]] |
| sublib/other.js:6:11:6:14 | path |
| sublib/other.js:6:11:6:17 | path[0] |
| sublib/sub.js:1:37:1:40 | path |
| sublib/sub.js:1:37:1:40 | path |
| sublib/sub.js:2:3:2:14 | obj[path[0]] |
@@ -289,6 +295,11 @@ edges
| lib.js:128:13:128:16 | path | lib.js:128:13:128:19 | path[0] |
| lib.js:128:13:128:19 | path[0] | lib.js:128:9:128:20 | obj[path[0]] |
| lib.js:128:13:128:19 | path[0] | lib.js:128:9:128:20 | obj[path[0]] |
| sublib/other.js:5:28:5:31 | path | sublib/other.js:6:11:6:14 | path |
| sublib/other.js:5:28:5:31 | path | sublib/other.js:6:11:6:14 | path |
| sublib/other.js:6:11:6:14 | path | sublib/other.js:6:11:6:17 | path[0] |
| sublib/other.js:6:11:6:17 | path[0] | sublib/other.js:6:7:6:18 | obj[path[0]] |
| sublib/other.js:6:11:6:17 | path[0] | sublib/other.js:6:7:6:18 | obj[path[0]] |
| sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:7:2:10 | path |
| sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:7:2:10 | path |
| sublib/sub.js:2:7:2:10 | path | sublib/sub.js:2:7:2:13 | path[0] |
@@ -356,6 +367,7 @@ edges
| lib.js:108:3:108:10 | obj[one] | lib.js:104:13:104:21 | arguments | lib.js:108:3:108:10 | obj[one] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:104:13:104:21 | arguments | library input |
| lib.js:119:13:119:24 | obj[path[0]] | lib.js:118:29:118:32 | path | lib.js:119:13:119:24 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:118:29:118:32 | path | library input |
| lib.js:128:9:128:20 | obj[path[0]] | lib.js:127:14:127:17 | path | lib.js:128:9:128:20 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | lib.js:127:14:127:17 | path | library input |
| sublib/other.js:6:7:6:18 | obj[path[0]] | sublib/other.js:5:28:5:31 | path | sublib/other.js:6:7:6:18 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | sublib/other.js:5:28:5:31 | path | library input |
| sublib/sub.js:2:3:2:14 | obj[path[0]] | sublib/sub.js:1:37:1:40 | path | sublib/sub.js:2:3:2:14 | obj[path[0]] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | sublib/sub.js:1:37:1:40 | path | library input |
| tst.js:8:5:8:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:8:5:8:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |
| tst.js:9:5:9:17 | object[taint] | tst.js:5:24:5:37 | req.query.data | tst.js:9:5:9:17 | object[taint] | This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@. | tst.js:5:24:5:37 | req.query.data | user controlled input |

View File

@@ -0,0 +1,15 @@
(function () {
function Foobar() {}
Foobar.prototype = {
method: function (obj, path, value) {
obj[path[0]][path[1]] = value; // NOT OK
},
};
module.exports.foobar = Foobar;
module.other.notExported = function (obj, path, value) {
obj[path[0]][path[1]] = value; // OK - not exported
}
})();

View File

@@ -1,3 +1,6 @@
module.exports.set = function (obj, path, value) {
obj[path[0]][path[1]] = value; // NOT OK
}
}
var other = require('./other')
exports.foobar = other.foobar;

View File

@@ -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() }

View File

@@ -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()

View File

@@ -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. */

View File

@@ -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,6 +24,22 @@ 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() {
@@ -63,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
}
}
/**
@@ -141,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()
}
}
/**

View File

@@ -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() }
}

View File

@@ -119,11 +119,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`.
@@ -309,7 +325,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
@@ -338,7 +354,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
@@ -349,7 +365,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)
@@ -396,7 +417,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
@@ -1272,7 +1293,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"]
)
)
}

View File

@@ -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() }
@@ -177,6 +226,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)
)
}
}
/**
@@ -196,18 +319,76 @@ 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.
result.(SsaDefinitionNode).getDefinition().(Ssa::SelfDefinition).getSourceVariable() =
access.(Namespace).getModuleSelfVariable()
or
not access instanceof Namespace and
result.asExpr().getExpr() = access
}
cached
predicate forceCachingInSameStage() { any() }
cached
predicate forceCachingBackref() { exists(any(ConstRef const).getConstant(_)) }
}
private import Cached
/** Gets a node corresponding to expression `e`. */
ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e }
@@ -562,3 +743,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 parent module of `A::B` is `A`, and `A` itself has no parent module.
*/
ModuleNode getParentModule() { 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 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 getNestedModule(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::getConstant` 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::getConstant`, never `ConstRef.getConstant`.
*/
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`.
*/
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::getConstant("A").getConstant("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 getConstant(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::getConstant("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 `getConstant()` one or more times on the result.
*
* For example `DataFlow::getConstant("A").getConstant("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 getConstant(string name) {
result.getName() = name and
result.isPossiblyGlobal()
}

View File

@@ -1,6 +1,7 @@
private import codeql.ssa.Ssa as SsaImplCommon
private import codeql.ruby.AST
private import codeql.ruby.CFG as Cfg
private import codeql.ruby.controlflow.internal.ControlFlowGraphImplShared as ControlFlowGraphImplShared
private import codeql.ruby.ast.Variable
private import Cfg::CfgNodes::ExprNodes
@@ -53,6 +54,9 @@ private module SsaInput implements SsaImplCommon::InputSig {
or
capturedExitRead(bb, i, v) and
certain = false
or
namespaceSelfExitRead(bb, i, v) and
certain = false
}
}
@@ -94,6 +98,21 @@ private predicate capturedExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, Local
i = bb.length()
}
/**
* Holds if a pseudo read of namespace self-variable `v` should be inserted
* at index `i` in basic block `bb`. We do this to ensure that namespace
* self-variables always get an SSA definition.
*/
private predicate namespaceSelfExitRead(Cfg::AnnotatedExitBasicBlock bb, int i, SelfVariable v) {
exists(Namespace ns, AstNode last |
v.getDeclaringScope() = ns and
last = ControlFlowGraphImplShared::getAControlFlowExitNode(ns) and
if last = ns
then bb.getNode(i).getAPredecessor().getNode() = last
else bb.getNode(i).getNode() = last
)
}
/**
* Holds if captured variable `v` is read directly inside `scope`,
* or inside a (transitively) nested scope of `scope`.

View File

@@ -105,6 +105,8 @@ private module Cached {
exists(DataFlow::ContentSet c | readStep(nodeFrom, c, nodeTo) |
c.isSingleton(any(DataFlow::Content::ElementContent ec))
or
c.isKnownOrUnknownElement(_)
or
c.isAnyElement()
)
}

View File

@@ -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::getConstant("ActionController").getConstant("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::getConstant("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::getConstant("ActionController").getConstant("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) {
@@ -626,9 +644,8 @@ private module ParamsSummaries {
override MethodCall getACall() {
result.getMethodName() = ["merge!", "reverse_merge!", "with_defaults!", "reverse_update"] 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) {
@@ -644,15 +661,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) }
@@ -662,10 +676,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) }
@@ -678,19 +689,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() {
@@ -703,12 +707,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() {

View File

@@ -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

View File

@@ -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::getConstant("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().getConstant(["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().getConstant("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" }

View File

@@ -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::getConstant("Rails").getConstant("Generators").getConstant("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) }

View File

@@ -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)
}
/**

View File

@@ -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()
)
}

View File

@@ -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"
])
}
}

View File

@@ -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

View File

@@ -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 |

View File

@@ -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 : |

View File

@@ -0,0 +1,17 @@
| captured_variables.rb:9:14:9:14 | x | Fixed missing result:hasValueFlow=1.2 |
| captured_variables.rb:16:14:16:14 | x | Fixed missing result:hasValueFlow=1.3 |
| instance_variables.rb:20:16:20:33 | # $ hasValueFlow=7 | Missing result:hasValueFlow=7 |
| instance_variables.rb:25:21:25:39 | # $ hasValueFlow=42 | Missing result:hasValueFlow=42 |
| instance_variables.rb:37:22:37:40 | # $ hasValueFlow=21 | Missing result:hasValueFlow=21 |
| instance_variables.rb:41:18:41:36 | # $ hasValueFlow=22 | Missing result:hasValueFlow=22 |
| instance_variables.rb:49:22:49:40 | # $ hasValueFlow=24 | Missing result:hasValueFlow=24 |
| instance_variables.rb:53:22:53:40 | # $ hasValueFlow=22 | Missing result:hasValueFlow=22 |
| instance_variables.rb:54:22:54:40 | # $ hasValueFlow=24 | Missing result:hasValueFlow=24 |
| instance_variables.rb:55:22:55:40 | # $ hasValueFlow=25 | Missing result:hasValueFlow=25 |
| instance_variables.rb:60:22:60:40 | # $ hasValueFlow=26 | Missing result:hasValueFlow=26 |
| instance_variables.rb:61:22:61:40 | # $ hasValueFlow=26 | Missing result:hasValueFlow=26 |
| instance_variables.rb:66:22:66:40 | # $ hasValueFlow=27 | Missing result:hasValueFlow=27 |
| instance_variables.rb:67:23:67:41 | # $ hasValueFlow=27 | Missing result:hasValueFlow=27 |
| instance_variables.rb:75:23:75:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |
| instance_variables.rb:79:23:79:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |
| instance_variables.rb:84:23:84:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 |

View File

@@ -0,0 +1 @@
import TestUtilities.InlineTypeTrackingFlowTest

View File

@@ -0,0 +1,38 @@
def capture_local_call x
fn = -> { sink(x) } # $ hasValueFlow=1.1
fn.call
end
capture_local_call source(1.1)
def capture_escape_return1 x
-> {
sink(x) # $ MISSING: hasValueFlow=1.2
}
end
(capture_escape_return1 source(1.2)).call
def capture_escape_return2 x
-> {
sink(x) # $ MISSING: hasValueFlow=1.3
}
end
Something.unknownMethod(capture_escape_return2 source(1.3))
def capture_escape_unknown_call x
fn = -> {
sink(x) # $ hasValueFlow=1.4
}
Something.unknownMethod(fn)
end
capture_escape_unknown_call source(1.4)
def call_it fn
fn.call
end
def capture_escape_known_call x
fn = -> {
sink(x) # $ hasValueFlow=1.5
}
call_it fn
end
capture_escape_known_call source(1.5)

View File

@@ -0,0 +1,234 @@
getAnAncestorExpr
| tst.rb:8:1:11:3 | C2 | tst.rb:8:12:8:13 | C1 |
| tst.rb:27:1:35:3 | C3 | tst.rb:27:12:27:13 | C2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:28:13:28:17 | Mixin |
| tst.rb:27:1:35:3 | C3 | tst.rb:29:13:29:18 | Mixin2 |
| tst.rb:41:5:42:7 | N1::XY1 | tst.rb:41:17:41:20 | Y |
| tst.rb:44:9:45:11 | N1::N2::XY2 | tst.rb:44:21:44:24 | Y |
| tst.rb:49:1:51:3 | N2 | tst.rb:50:13:50:13 | X |
| tst.rb:53:5:54:7 | N2::XY3 | tst.rb:53:17:53:17 | Y |
getAnAncestor
| file://:0:0:0:0 | Array | file://:0:0:0:0 | Array |
| file://:0:0:0:0 | Array | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Array | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Array | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | BasicObject | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Class | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Class | file://:0:0:0:0 | Class |
| file://:0:0:0:0 | Class | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Class | file://:0:0:0:0 | Module |
| file://:0:0:0:0 | Class | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Complex |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | Complex | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | FalseClass | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | FalseClass | file://:0:0:0:0 | FalseClass |
| file://:0:0:0:0 | FalseClass | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | FalseClass | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Float | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Float | file://:0:0:0:0 | Float |
| file://:0:0:0:0 | Float | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Float | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | Float | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Hash | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Hash | file://:0:0:0:0 | Hash |
| file://:0:0:0:0 | Hash | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Hash | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Integer | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Integer | file://:0:0:0:0 | Integer |
| file://:0:0:0:0 | Integer | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Integer | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | Integer | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Kernel | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Module | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Module | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Module | file://:0:0:0:0 | Module |
| file://:0:0:0:0 | Module | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | NilClass | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | NilClass | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | NilClass | file://:0:0:0:0 | NilClass |
| file://:0:0:0:0 | NilClass | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Numeric | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Numeric | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Numeric | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | Numeric | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Object | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Object | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Object | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Proc | file://:0:0:0:0 | Proc |
| file://:0:0:0:0 | Rational | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | Rational | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | Rational | file://:0:0:0:0 | Numeric |
| file://:0:0:0:0 | Rational | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | Rational | file://:0:0:0:0 | Rational |
| file://:0:0:0:0 | String | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | String | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | String | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | String | file://:0:0:0:0 | String |
| file://:0:0:0:0 | Symbol | file://:0:0:0:0 | Symbol |
| file://:0:0:0:0 | TrueClass | file://:0:0:0:0 | BasicObject |
| file://:0:0:0:0 | TrueClass | file://:0:0:0:0 | Kernel |
| file://:0:0:0:0 | TrueClass | file://:0:0:0:0 | Object |
| file://:0:0:0:0 | TrueClass | file://:0:0:0:0 | TrueClass |
| tst.rb:1:1:6:3 | C1 | file://:0:0:0:0 | BasicObject |
| tst.rb:1:1:6:3 | C1 | file://:0:0:0:0 | Kernel |
| tst.rb:1:1:6:3 | C1 | file://:0:0:0:0 | Object |
| tst.rb:1:1:6:3 | C1 | tst.rb:1:1:6:3 | C1 |
| tst.rb:8:1:11:3 | C2 | file://:0:0:0:0 | BasicObject |
| tst.rb:8:1:11:3 | C2 | file://:0:0:0:0 | Kernel |
| tst.rb:8:1:11:3 | C2 | file://:0:0:0:0 | Object |
| tst.rb:8:1:11:3 | C2 | tst.rb:1:1:6:3 | C1 |
| tst.rb:8:1:11:3 | C2 | tst.rb:8:1:11:3 | C2 |
| tst.rb:13:1:18:3 | Mixin | tst.rb:13:1:18:3 | Mixin |
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:20:1:25:3 | Mixin2 |
| tst.rb:27:1:35:3 | C3 | file://:0:0:0:0 | BasicObject |
| tst.rb:27:1:35:3 | C3 | file://:0:0:0:0 | Kernel |
| tst.rb:27:1:35:3 | C3 | file://:0:0:0:0 | Object |
| tst.rb:27:1:35:3 | C3 | tst.rb:1:1:6:3 | C1 |
| tst.rb:27:1:35:3 | C3 | tst.rb:8:1:11:3 | C2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:13:1:18:3 | Mixin |
| tst.rb:27:1:35:3 | C3 | tst.rb:20:1:25:3 | Mixin2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:27:1:35:3 | C3 |
| tst.rb:40:1:47:3 | N1 | tst.rb:40:1:47:3 | N1 |
| tst.rb:41:5:42:7 | N1::XY1 | file://:0:0:0:0 | BasicObject |
| tst.rb:41:5:42:7 | N1::XY1 | file://:0:0:0:0 | Kernel |
| tst.rb:41:5:42:7 | N1::XY1 | file://:0:0:0:0 | Object |
| tst.rb:41:5:42:7 | N1::XY1 | tst.rb:41:5:42:7 | N1::XY1 |
| tst.rb:43:5:46:7 | N1::N2 | tst.rb:43:5:46:7 | N1::N2 |
| tst.rb:44:9:45:11 | N1::N2::XY2 | file://:0:0:0:0 | BasicObject |
| tst.rb:44:9:45:11 | N1::N2::XY2 | file://:0:0:0:0 | Kernel |
| tst.rb:44:9:45:11 | N1::N2::XY2 | file://:0:0:0:0 | Object |
| tst.rb:44:9:45:11 | N1::N2::XY2 | tst.rb:44:9:45:11 | N1::N2::XY2 |
| tst.rb:49:1:51:3 | N2 | tst.rb:49:1:51:3 | N2 |
| tst.rb:53:5:54:7 | N2::XY3 | file://:0:0:0:0 | BasicObject |
| tst.rb:53:5:54:7 | N2::XY3 | file://:0:0:0:0 | Kernel |
| tst.rb:53:5:54:7 | N2::XY3 | file://:0:0:0:0 | Object |
| tst.rb:53:5:54:7 | N2::XY3 | tst.rb:53:5:54:7 | N2::XY3 |
| tst.rb:57:1:62:3 | Nodes | file://:0:0:0:0 | BasicObject |
| tst.rb:57:1:62:3 | Nodes | file://:0:0:0:0 | Kernel |
| tst.rb:57:1:62:3 | Nodes | file://:0:0:0:0 | Object |
| tst.rb:57:1:62:3 | Nodes | tst.rb:57:1:62:3 | Nodes |
getModuleLevelSelf
| tst.rb:1:1:6:3 | C1 | tst.rb:1:1:6:3 | self (C1) |
| tst.rb:8:1:11:3 | C2 | tst.rb:8:1:11:3 | self (C2) |
| tst.rb:13:1:18:3 | Mixin | tst.rb:13:1:18:3 | self (Mixin) |
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:20:1:25:3 | self (Mixin2) |
| tst.rb:27:1:35:3 | C3 | tst.rb:27:1:35:3 | self (C3) |
| tst.rb:40:1:47:3 | N1 | tst.rb:40:1:47:3 | self (N1) |
| tst.rb:41:5:42:7 | N1::XY1 | tst.rb:41:5:42:7 | self (XY1) |
| tst.rb:43:5:46:7 | N1::N2 | tst.rb:43:5:46:7 | self (N2) |
| tst.rb:44:9:45:11 | N1::N2::XY2 | tst.rb:44:9:45:11 | self (XY2) |
| tst.rb:49:1:51:3 | N2 | tst.rb:49:1:51:3 | self (N2) |
| tst.rb:49:1:51:3 | N2 | tst.rb:52:1:55:3 | self (N2) |
| tst.rb:53:5:54:7 | N2::XY3 | tst.rb:53:5:54:7 | self (XY3) |
| tst.rb:57:1:62:3 | Nodes | tst.rb:57:1:62:3 | self (Nodes) |
getAnImmediateReference
| file://:0:0:0:0 | Array | tst.rb:59:15:59:21 | Array |
| file://:0:0:0:0 | Hash | tst.rb:60:14:60:45 | Hash |
| tst.rb:1:1:6:3 | C1 | tst.rb:8:12:8:13 | C1 |
| tst.rb:8:1:11:3 | C2 | tst.rb:27:12:27:13 | C2 |
| tst.rb:13:1:18:3 | Mixin | tst.rb:28:13:28:17 | Mixin |
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:29:13:29:18 | Mixin2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:37:5:37:6 | C3 |
getOwnInstanceMethod
| tst.rb:1:1:6:3 | C1 | c1 | tst.rb:2:5:5:7 | c1 |
| tst.rb:8:1:11:3 | C2 | c2 | tst.rb:9:5:10:7 | c2 |
| tst.rb:13:1:18:3 | Mixin | m1 | tst.rb:14:5:15:7 | m1 |
| tst.rb:20:1:25:3 | Mixin2 | m2 | tst.rb:21:5:22:7 | m2 |
| tst.rb:57:1:62:3 | Nodes | m1 | tst.rb:58:5:61:7 | m1 |
getInstanceMethod
| tst.rb:1:1:6:3 | C1 | c1 | tst.rb:2:5:5:7 | c1 |
| tst.rb:8:1:11:3 | C2 | c1 | tst.rb:2:5:5:7 | c1 |
| tst.rb:8:1:11:3 | C2 | c2 | tst.rb:9:5:10:7 | c2 |
| tst.rb:13:1:18:3 | Mixin | m1 | tst.rb:14:5:15:7 | m1 |
| tst.rb:20:1:25:3 | Mixin2 | m2 | tst.rb:21:5:22:7 | m2 |
| tst.rb:27:1:35:3 | C3 | c1 | tst.rb:2:5:5:7 | c1 |
| tst.rb:27:1:35:3 | C3 | c2 | tst.rb:9:5:10:7 | c2 |
| tst.rb:27:1:35:3 | C3 | m1 | tst.rb:14:5:15:7 | m1 |
| tst.rb:27:1:35:3 | C3 | m2 | tst.rb:21:5:22:7 | m2 |
| tst.rb:57:1:62:3 | Nodes | m1 | tst.rb:58:5:61:7 | m1 |
getAnOwnInstanceSelf
| tst.rb:1:1:6:3 | C1 | tst.rb:2:5:5:7 | self in c1 |
| tst.rb:8:1:11:3 | C2 | tst.rb:9:5:10:7 | self in c2 |
| tst.rb:13:1:18:3 | Mixin | tst.rb:14:5:15:7 | self in m1 |
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:21:5:22:7 | self in m2 |
| tst.rb:57:1:62:3 | Nodes | tst.rb:58:5:61:7 | self in m1 |
getAnInstanceSelf
| tst.rb:1:1:6:3 | C1 | tst.rb:2:5:5:7 | self in c1 |
| tst.rb:8:1:11:3 | C2 | tst.rb:2:5:5:7 | self in c1 |
| tst.rb:8:1:11:3 | C2 | tst.rb:9:5:10:7 | self in c2 |
| tst.rb:13:1:18:3 | Mixin | tst.rb:14:5:15:7 | self in m1 |
| tst.rb:20:1:25:3 | Mixin2 | tst.rb:21:5:22:7 | self in m2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:2:5:5:7 | self in c1 |
| tst.rb:27:1:35:3 | C3 | tst.rb:9:5:10:7 | self in c2 |
| tst.rb:27:1:35:3 | C3 | tst.rb:14:5:15:7 | self in m1 |
| tst.rb:27:1:35:3 | C3 | tst.rb:21:5:22:7 | self in m2 |
| tst.rb:57:1:62:3 | Nodes | tst.rb:58:5:61:7 | self in m1 |
getOwnSingletonMethod
| tst.rb:13:1:18:3 | Mixin | m1s | tst.rb:16:5:17:7 | m1s |
| tst.rb:20:1:25:3 | Mixin2 | m2s | tst.rb:23:5:24:7 | m2s |
| tst.rb:27:1:35:3 | C3 | c3_self1 | tst.rb:32:9:33:11 | c3_self1 |
| tst.rb:27:1:35:3 | C3 | c3_self2 | tst.rb:37:1:38:3 | c3_self2 |
getAnOwnInstanceVariableRead
| tst.rb:1:1:6:3 | C1 | @field | tst.rb:4:9:4:14 | @field |
getAnOwnInstanceVariableWriteValue
| tst.rb:1:1:6:3 | C1 | @field | tst.rb:3:18:3:18 | 1 |
getParentModule
| tst.rb:41:5:42:7 | N1::XY1 | tst.rb:40:1:47:3 | N1 |
| tst.rb:43:5:46:7 | N1::N2 | tst.rb:40:1:47:3 | N1 |
| tst.rb:44:9:45:11 | N1::N2::XY2 | tst.rb:43:5:46:7 | N1::N2 |
| tst.rb:53:5:54:7 | N2::XY3 | tst.rb:49:1:51:3 | N2 |
getNestedModule
| tst.rb:40:1:47:3 | N1 | N2 | tst.rb:43:5:46:7 | N1::N2 |
| tst.rb:40:1:47:3 | N1 | XY1 | tst.rb:41:5:42:7 | N1::XY1 |
| tst.rb:43:5:46:7 | N1::N2 | XY2 | tst.rb:44:9:45:11 | N1::N2::XY2 |
| tst.rb:49:1:51:3 | N2 | XY3 | tst.rb:53:5:54:7 | N2::XY3 |
getTopLevelConst
| Array | tst.rb:59:15:59:21 | Array |
| C1 | tst.rb:1:1:6:3 | self (C1) |
| C1 | tst.rb:8:12:8:13 | C1 |
| C2 | tst.rb:8:1:11:3 | self (C2) |
| C2 | tst.rb:27:12:27:13 | C2 |
| C3 | tst.rb:27:1:35:3 | self (C3) |
| C3 | tst.rb:37:5:37:6 | C3 |
| Hash | tst.rb:60:14:60:45 | Hash |
| Mixin | tst.rb:13:1:18:3 | self (Mixin) |
| Mixin | tst.rb:28:13:28:17 | Mixin |
| Mixin2 | tst.rb:20:1:25:3 | self (Mixin2) |
| Mixin2 | tst.rb:29:13:29:18 | Mixin2 |
| N1 | tst.rb:40:1:47:3 | self (N1) |
| N2 | tst.rb:43:5:46:7 | self (N2) |
| N2 | tst.rb:49:1:51:3 | self (N2) |
| N2 | tst.rb:52:1:55:3 | self (N2) |
| Nodes | tst.rb:57:1:62:3 | self (Nodes) |
| X | tst.rb:41:17:41:17 | X |
| X | tst.rb:44:21:44:21 | X |
| X | tst.rb:50:13:50:13 | X |
| XY1 | tst.rb:41:5:42:7 | self (XY1) |
| XY2 | tst.rb:44:9:45:11 | self (XY2) |
| XY3 | tst.rb:53:5:54:7 | self (XY3) |
| Y | tst.rb:53:17:53:17 | Y |
getConstant
| tst.rb:41:17:41:17 | X | Y | tst.rb:41:17:41:20 | Y |
| tst.rb:44:21:44:21 | X | Y | tst.rb:44:21:44:24 | Y |
| tst.rb:50:13:50:13 | X | X | tst.rb:50:13:50:13 | X |
| tst.rb:50:13:50:13 | X | XY3 | tst.rb:53:5:54:7 | self (XY3) |
| tst.rb:50:13:50:13 | X | Y | tst.rb:53:17:53:17 | Y |
getXYClasses
| tst.rb:41:5:42:7 | N1::XY1 |
| tst.rb:44:9:45:11 | N1::N2::XY2 |
| tst.rb:53:5:54:7 | N2::XY3 |
hashLiteralNode
| tst.rb:60:14:60:45 | call to [] |
hashLiteralKey
| tst.rb:60:14:60:45 | call to [] | bar | tst.rb:60:36:60:36 | 2 |
| tst.rb:60:14:60:45 | call to [] | baz | tst.rb:60:44:60:44 | 3 |
| tst.rb:60:14:60:45 | call to [] | foo | tst.rb:60:24:60:24 | 1 |
arrayLiteralNode
| tst.rb:59:15:59:21 | call to [] |
arrayLiteralElement
| tst.rb:59:15:59:21 | call to [] | tst.rb:59:16:59:16 | 1 |
| tst.rb:59:15:59:21 | call to [] | tst.rb:59:18:59:18 | 2 |
| tst.rb:59:15:59:21 | call to [] | tst.rb:59:20:59:20 | 3 |

View File

@@ -0,0 +1,73 @@
import ruby
query DataFlow::Node getAnAncestorExpr(DataFlow::ModuleNode mod) {
result = mod.getAnAncestorExpr()
}
query DataFlow::ModuleNode getAnAncestor(DataFlow::ModuleNode mod) { result = mod.getAnAncestor() }
query DataFlow::Node getModuleLevelSelf(DataFlow::ModuleNode mod) {
result = mod.getModuleLevelSelf()
}
query DataFlow::Node getAnImmediateReference(DataFlow::ModuleNode mod) {
result = mod.getAnImmediateReference()
}
query DataFlow::MethodNode getOwnInstanceMethod(DataFlow::ModuleNode mod, string name) {
result = mod.getOwnInstanceMethod(name)
}
query DataFlow::MethodNode getInstanceMethod(DataFlow::ModuleNode mod, string name) {
result = mod.getInstanceMethod(name)
}
query DataFlow::Node getAnOwnInstanceSelf(DataFlow::ModuleNode mod) {
result = mod.getAnOwnInstanceSelf()
}
query DataFlow::Node getAnInstanceSelf(DataFlow::ModuleNode mod) {
result = mod.getAnInstanceSelf()
}
query DataFlow::Node getOwnSingletonMethod(DataFlow::ModuleNode mod, string name) {
result = mod.getOwnSingletonMethod(name)
}
query DataFlow::Node getAnOwnInstanceVariableRead(DataFlow::ModuleNode mod, string name) {
result = mod.getAnOwnInstanceVariableRead(name)
}
query DataFlow::Node getAnOwnInstanceVariableWriteValue(DataFlow::ModuleNode mod, string name) {
result = mod.getAnOwnInstanceVariableWriteValue(name)
}
query DataFlow::ModuleNode getParentModule(DataFlow::ModuleNode mod) {
result = mod.getParentModule()
}
query DataFlow::ModuleNode getNestedModule(DataFlow::ModuleNode mod, string name) {
result = mod.getNestedModule(name)
}
query DataFlow::Node getTopLevelConst(string name) { result = DataFlow::getConstant(name) }
query DataFlow::Node getConstant(DataFlow::ConstRef base, string name) {
result = base.getConstant(name)
}
query DataFlow::ModuleNode getXYClasses() {
result = DataFlow::getConstant("X").getConstant("Y").getADescendentModule()
}
query DataFlow::HashLiteralNode hashLiteralNode() { any() }
query DataFlow::Node hashLiteralKey(DataFlow::HashLiteralNode node, string key) {
result = node.getElementFromKey(Ast::ConstantValue::fromStringlikeValue(key))
}
query DataFlow::ArrayLiteralNode arrayLiteralNode() { any() }
query DataFlow::Node arrayLiteralElement(DataFlow::ArrayLiteralNode node) {
result = node.getAnElement()
}

View File

@@ -0,0 +1,62 @@
module C1
def c1
@field = 1
@field
end
end
class C2 < C1
def c2
end
end
module Mixin
def m1
end
def self.m1s
end
end
module Mixin2
def m2
end
def self.m2s
end
end
class C3 < C2
include Mixin
prepend Mixin2
class << self
def c3_self1
end
end
end
def C3.c3_self2
end
module N1
class XY1 < X::Y
end
module N2
class XY2 < X::Y
end
end
end
module N2
include X
end
module N2
class XY3 < Y
end
end
class Nodes
def m1
array=[1,2,3]
hash={'foo' => 1, 'bar' => 2, baz: 3}
end
end

View File

@@ -26,7 +26,7 @@
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... = ... | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:5:13:3 | <captured> | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | <captured> self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
@@ -65,7 +65,7 @@
| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return |
| local_dataflow.rb:49:1:53:3 | [post] self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:3:53:3 | <captured> | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:49:3:53:3 | <captured> x | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:50:8:50:13 | "next" | local_dataflow.rb:50:3:50:13 | next |
| local_dataflow.rb:50:18:50:18 | [post] x | local_dataflow.rb:51:20:51:20 | x |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:51:20:51:20 | x |
@@ -264,7 +264,7 @@
| local_dataflow.rb:118:3:118:11 | [post] self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:3:118:11 | call to source | local_dataflow.rb:118:3:118:31 | call to tap |
| local_dataflow.rb:118:3:118:11 | self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:17:118:31 | <captured> | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:17:118:31 | <captured> self | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:28:118:28 | x |
| local_dataflow.rb:119:3:119:31 | [post] self | local_dataflow.rb:119:8:119:16 | self |
@@ -278,4 +278,4 @@
| local_dataflow.rb:123:8:123:16 | call to source | local_dataflow.rb:123:8:123:20 | call to dup |
| local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap |
| local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup |
| local_dataflow.rb:123:26:123:45 | <captured> | local_dataflow.rb:123:32:123:43 | self |
| local_dataflow.rb:123:26:123:45 | <captured> self | local_dataflow.rb:123:32:123:43 | self |

View File

@@ -1,3 +1,4 @@
| file://:0:0:0:0 | [summary] read: argument position 0.any element in Hash[] | file://:0:0:0:0 | [summary] read: argument position 0.any element.element 1 or unknown in Hash[] |
| file://:0:0:0:0 | parameter any of ;Pathname;Method[join] | file://:0:0:0:0 | [summary] to write: return (return) in ;Pathname;Method[join] |
| file://:0:0:0:0 | parameter position 0 of & | file://:0:0:0:0 | [summary] read: argument position 0.any element in & |
| file://:0:0:0:0 | parameter position 0 of + | file://:0:0:0:0 | [summary] read: argument position 0.any element in + |
@@ -78,7 +79,7 @@
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:9:9:9:15 | call to [] | local_dataflow.rb:9:1:9:15 | ... = ... |
| local_dataflow.rb:10:5:13:3 | ... = ... | local_dataflow.rb:12:5:12:5 | x |
| local_dataflow.rb:10:5:13:3 | <captured> | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | <captured> self | local_dataflow.rb:11:1:11:2 | self |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | ... = ... |
| local_dataflow.rb:10:5:13:3 | __synth__0__1 | local_dataflow.rb:10:5:13:3 | __synth__0__1 |
@@ -123,7 +124,7 @@
| local_dataflow.rb:45:10:45:10 | 6 | local_dataflow.rb:45:3:45:10 | return |
| local_dataflow.rb:49:1:53:3 | [post] self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:1:53:3 | self | local_dataflow.rb:55:1:55:14 | self |
| local_dataflow.rb:49:3:53:3 | <captured> | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:49:3:53:3 | <captured> x | local_dataflow.rb:50:18:50:18 | x |
| local_dataflow.rb:50:8:50:13 | "next" | local_dataflow.rb:50:3:50:13 | next |
| local_dataflow.rb:50:18:50:18 | [post] x | local_dataflow.rb:51:20:51:20 | x |
| local_dataflow.rb:50:18:50:18 | x | local_dataflow.rb:50:18:50:22 | ... < ... |
@@ -338,7 +339,7 @@
| local_dataflow.rb:118:3:118:11 | [post] self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:3:118:11 | call to source | local_dataflow.rb:118:3:118:31 | call to tap |
| local_dataflow.rb:118:3:118:11 | self | local_dataflow.rb:119:3:119:31 | self |
| local_dataflow.rb:118:17:118:31 | <captured> | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:17:118:31 | <captured> self | local_dataflow.rb:118:23:118:29 | self |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:20:118:20 | x |
| local_dataflow.rb:118:20:118:20 | x | local_dataflow.rb:118:28:118:28 | x |
| local_dataflow.rb:119:3:119:31 | [post] self | local_dataflow.rb:119:8:119:16 | self |
@@ -352,4 +353,4 @@
| local_dataflow.rb:123:8:123:16 | call to source | local_dataflow.rb:123:8:123:20 | call to dup |
| local_dataflow.rb:123:8:123:20 | call to dup | local_dataflow.rb:123:8:123:45 | call to tap |
| local_dataflow.rb:123:8:123:45 | call to tap | local_dataflow.rb:123:8:123:49 | call to dup |
| local_dataflow.rb:123:26:123:45 | <captured> | local_dataflow.rb:123:32:123:43 | self |
| local_dataflow.rb:123:26:123:45 | <captured> self | local_dataflow.rb:123:32:123:43 | self |

View File

@@ -112,7 +112,7 @@ private class TypeFromCodeQL extends ModelInput::TypeModel {
override DataFlow::Node getASource(string package, string type) {
package = "test" and
type = "FooOrBar" and
result.asExpr().getExpr().getConstantValue().getString() = "magic_string"
result.getConstantValue().getString() = "magic_string"
}
override API::Node getAnApiNode(string package, string type) {

View File

@@ -1,4 +1,5 @@
track
| type_tracker.rb:1:1:10:3 | self (Container) | type tracker without call steps | type_tracker.rb:1:1:10:3 | self (Container) |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:18:1:21:3 | self in positional |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type tracker with call steps | type_tracker.rb:25:1:28:3 | self (keyword) |
@@ -18,13 +19,19 @@ track
| type_tracker.rb:2:16:2:18 | val | type tracker with call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker with call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:2:16:2:18 | val | type tracker without call steps | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:3:9:3:23 | call to puts | type tracker without call steps | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:14:3:23 | call to field | type tracker without call steps | type_tracker.rb:3:14:3:23 | call to field |
@@ -189,20 +196,33 @@ track
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker with call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:36:5:36:10 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:40:5:40:12 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:38:13:38:25 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:44:5:44:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:50:14:50:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element | type_tracker.rb:52:5:52:13 | ...[...] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:35:11:35:15 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:42:14:42:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
| type_tracker.rb:34:18:34:20 | obj | type tracker without call steps with content element 0 or unknown | type_tracker.rb:46:14:46:26 | call to [] |
| type_tracker.rb:34:23:34:23 | y | type tracker with call steps | type_tracker.rb:34:23:34:23 | y |
| type_tracker.rb:34:23:34:23 | y | type tracker without call steps | type_tracker.rb:34:23:34:23 | y |
@@ -337,6 +357,7 @@ track
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:52:5:52:13 | ...[...] | type tracker without call steps | type_tracker.rb:52:5:52:13 | ...[...] |
trackEnd
| type_tracker.rb:1:1:10:3 | self (Container) | type_tracker.rb:1:1:10:3 | self (Container) |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:1:1:10:3 | self (type_tracker.rb) |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:18:1:21:3 | self (positional) |
| type_tracker.rb:1:1:10:3 | self (type_tracker.rb) | type_tracker.rb:18:1:21:3 | self in positional |
@@ -370,19 +391,24 @@ trackEnd
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:7:5:9:7 | self in field |
| type_tracker.rb:2:5:5:7 | self in field= | type_tracker.rb:8:9:8:14 | self |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:5:5:7 | return return in field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:2:16:2:18 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:3:14:3:23 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:9:4:20 | ... = ... |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:9:4:20 | ... = ... |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:4:18:4:20 | val |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:7:5:9:7 | return return in field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:8:9:8:14 | @field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:14:5:14:13 | call to field= |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:2:16:2:18 | val | type_tracker.rb:15:10:15:18 | call to field |
| type_tracker.rb:3:9:3:23 | call to puts | type_tracker.rb:3:9:3:23 | call to puts |
| type_tracker.rb:3:14:3:23 | call to field | type_tracker.rb:3:14:3:23 | call to field |
@@ -565,6 +591,7 @@ trackEnd
| type_tracker.rb:34:1:53:3 | self in throughArray | type_tracker.rb:34:1:53:3 | self in throughArray |
| type_tracker.rb:34:1:53:3 | throughArray | type_tracker.rb:34:1:53:3 | throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:1:53:3 | return return in throughArray |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |
| type_tracker.rb:34:18:34:20 | obj | type_tracker.rb:34:18:34:20 | obj |

View File

@@ -22,7 +22,7 @@ query predicate trackEnd(LocalSourceNode src, DataFlow::Node dst) {
predicate backtrack(LocalSourceNode sink, TypeBackTracker t, LocalSourceNode src) {
t.start() and
sink = src
sink.getALocalSource() = src
or
exists(TypeBackTracker t2, LocalSourceNode mid |
backtrack(sink, t2, mid) and

View File

@@ -1,17 +1,18 @@
actionControllerControllerClasses
| action_controller/input_access.rb:1:1:50:3 | UsersController |
| action_controller/params_flow.rb:1:1:160:3 | MyController |
| action_controller/params_flow.rb:1:1:162:3 | MyController |
| action_controller/params_flow.rb:170:1:178:3 | Subclass |
| active_record/ActiveRecord.rb:23:1:39:3 | FooController |
| active_record/ActiveRecord.rb:41:1:64:3 | BarController |
| active_record/ActiveRecord.rb:66:1:98:3 | BazController |
| active_record/ActiveRecord.rb:100:1:108:3 | AnnotatedController |
| active_storage/active_storage.rb:39:1:45:3 | PostsController |
| active_storage/active_storage.rb:39:1:45:3 | PostsController2 |
| app/controllers/comments_controller.rb:1:1:40:3 | CommentsController |
| app/controllers/foo/bars_controller.rb:3:1:46:3 | BarsController |
| app/controllers/photos_controller.rb:1:1:4:3 | PhotosController |
| app/controllers/posts_controller.rb:1:1:10:3 | PostsController |
| app/controllers/tags_controller.rb:1:1:2:3 | TagsController |
| app/controllers/users/notifications_controller.rb:2:3:5:5 | NotificationsController |
| app/controllers/users/notifications_controller.rb:2:3:5:5 | Users::NotificationsController |
actionControllerActionMethods
| action_controller/input_access.rb:2:3:49:5 | index |
| action_controller/params_flow.rb:2:3:4:5 | m1 |
@@ -48,6 +49,7 @@ actionControllerActionMethods
| action_controller/params_flow.rb:134:3:141:5 | m31 |
| action_controller/params_flow.rb:143:3:150:5 | m32 |
| action_controller/params_flow.rb:152:3:159:5 | m33 |
| action_controller/params_flow.rb:171:3:173:5 | m34 |
| active_record/ActiveRecord.rb:27:3:38:5 | some_request_handler |
| active_record/ActiveRecord.rb:42:3:47:5 | some_other_request_handler |
| active_record/ActiveRecord.rb:49:3:63:5 | safe_paths |
@@ -121,6 +123,9 @@ paramsCalls
| action_controller/params_flow.rb:153:10:153:15 | call to params |
| action_controller/params_flow.rb:154:32:154:37 | call to params |
| action_controller/params_flow.rb:157:22:157:27 | call to params |
| action_controller/params_flow.rb:166:10:166:15 | call to params |
| action_controller/params_flow.rb:172:10:172:15 | call to params |
| action_controller/params_flow.rb:176:10:176:15 | call to params |
| action_mailer/mailer.rb:3:10:3:15 | call to params |
| active_record/ActiveRecord.rb:28:30:28:35 | call to params |
| active_record/ActiveRecord.rb:29:29:29:34 | call to params |
@@ -199,6 +204,9 @@ paramsSources
| action_controller/params_flow.rb:153:10:153:15 | call to params |
| action_controller/params_flow.rb:154:32:154:37 | call to params |
| action_controller/params_flow.rb:157:22:157:27 | call to params |
| action_controller/params_flow.rb:166:10:166:15 | call to params |
| action_controller/params_flow.rb:172:10:172:15 | call to params |
| action_controller/params_flow.rb:176:10:176:15 | call to params |
| action_mailer/mailer.rb:3:10:3:15 | call to params |
| active_record/ActiveRecord.rb:28:30:28:35 | call to params |
| active_record/ActiveRecord.rb:29:29:29:34 | call to params |
@@ -316,6 +324,9 @@ httpInputAccesses
| action_controller/params_flow.rb:153:10:153:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:154:32:154:37 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:157:22:157:27 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:166:10:166:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:172:10:172:15 | call to params | ActionController::Metal#params |
| action_controller/params_flow.rb:176:10:176:15 | call to params | ActionController::Metal#params |
| action_mailer/mailer.rb:3:10:3:15 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:28:30:28:35 | call to params | ActionController::Metal#params |
| active_record/ActiveRecord.rb:29:29:29:34 | call to params | ActionController::Metal#params |

View File

@@ -5,7 +5,7 @@ private import codeql.ruby.frameworks.ActionView
private import codeql.ruby.Concepts
private import codeql.ruby.DataFlow
query predicate actionControllerControllerClasses(ActionControllerControllerClass cls) { any() }
query predicate actionControllerControllerClasses(ActionControllerClass cls) { any() }
query predicate actionControllerActionMethods(ActionControllerActionMethod m) { any() }
@@ -25,11 +25,11 @@ query predicate redirectToCalls(RedirectToCall c) { any() }
query predicate actionControllerHelperMethods(ActionControllerHelperMethod m) { any() }
query predicate getAssociatedControllerClasses(ActionControllerControllerClass cls, ErbFile f) {
query predicate getAssociatedControllerClasses(ActionControllerClass cls, ErbFile f) {
cls = getAssociatedControllerClass(f)
}
query predicate controllerTemplateFiles(ActionControllerControllerClass cls, ErbFile templateFile) {
query predicate controllerTemplateFiles(ActionControllerClass cls, ErbFile templateFile) {
controllerTemplateFile(cls, templateFile)
}

View File

@@ -48,6 +48,9 @@ edges
| params_flow.rb:154:32:154:37 | call to params : | params_flow.rb:154:10:154:38 | call to reverse_update |
| params_flow.rb:157:5:157:5 | [post] p : | params_flow.rb:158:10:158:10 | p |
| params_flow.rb:157:22:157:27 | call to params : | params_flow.rb:157:5:157:5 | [post] p : |
| params_flow.rb:166:10:166:15 | call to params : | params_flow.rb:166:10:166:19 | ...[...] |
| params_flow.rb:172:10:172:15 | call to params : | params_flow.rb:172:10:172:19 | ...[...] |
| params_flow.rb:176:10:176:15 | call to params : | params_flow.rb:176:10:176:19 | ...[...] |
nodes
| params_flow.rb:3:10:3:15 | call to params : | semmle.label | call to params : |
| params_flow.rb:3:10:3:19 | ...[...] | semmle.label | ...[...] |
@@ -141,6 +144,12 @@ nodes
| params_flow.rb:157:5:157:5 | [post] p : | semmle.label | [post] p : |
| params_flow.rb:157:22:157:27 | call to params : | semmle.label | call to params : |
| params_flow.rb:158:10:158:10 | p | semmle.label | p |
| params_flow.rb:166:10:166:15 | call to params : | semmle.label | call to params : |
| params_flow.rb:166:10:166:19 | ...[...] | semmle.label | ...[...] |
| params_flow.rb:172:10:172:15 | call to params : | semmle.label | call to params : |
| params_flow.rb:172:10:172:19 | ...[...] | semmle.label | ...[...] |
| params_flow.rb:176:10:176:15 | call to params : | semmle.label | call to params : |
| params_flow.rb:176:10:176:19 | ...[...] | semmle.label | ...[...] |
subpaths
#select
| params_flow.rb:3:10:3:19 | ...[...] | params_flow.rb:3:10:3:15 | call to params : | params_flow.rb:3:10:3:19 | ...[...] | $@ | params_flow.rb:3:10:3:15 | call to params : | call to params : |
@@ -187,3 +196,6 @@ subpaths
| params_flow.rb:153:10:153:44 | call to reverse_update | params_flow.rb:153:10:153:15 | call to params : | params_flow.rb:153:10:153:44 | call to reverse_update | $@ | params_flow.rb:153:10:153:15 | call to params : | call to params : |
| params_flow.rb:154:10:154:38 | call to reverse_update | params_flow.rb:154:32:154:37 | call to params : | params_flow.rb:154:10:154:38 | call to reverse_update | $@ | params_flow.rb:154:32:154:37 | call to params : | call to params : |
| params_flow.rb:158:10:158:10 | p | params_flow.rb:157:22:157:27 | call to params : | params_flow.rb:158:10:158:10 | p | $@ | params_flow.rb:157:22:157:27 | call to params : | call to params : |
| params_flow.rb:166:10:166:19 | ...[...] | params_flow.rb:166:10:166:15 | call to params : | params_flow.rb:166:10:166:19 | ...[...] | $@ | params_flow.rb:166:10:166:15 | call to params : | call to params : |
| params_flow.rb:172:10:172:19 | ...[...] | params_flow.rb:172:10:172:15 | call to params : | params_flow.rb:172:10:172:19 | ...[...] | $@ | params_flow.rb:172:10:172:15 | call to params : | call to params : |
| params_flow.rb:176:10:176:19 | ...[...] | params_flow.rb:176:10:176:15 | call to params : | params_flow.rb:176:10:176:19 | ...[...] | $@ | params_flow.rb:176:10:176:15 | call to params : | call to params : |

View File

@@ -157,4 +157,22 @@ class MyController < ActionController::Base
p.reverse_update(params)
sink p # $hasTaintFlow
end
include Mixin
end
module Mixin
def m33
sink params[:x] # $hasTaintFlow
end
end
class Subclass < MyController
def m34
sink params[:x] # $hasTaintFlow
end
rescue_from 'Foo::Bar' do |err|
sink params[:x] # $hasTaintFlow
end
end

View File

@@ -36,7 +36,7 @@ ActiveStorage.video_preview_arguments = custom_preview_args
ActiveStorage.variant_processor = custom_processor
class PostsController < ActionController::Base
class PostsController2 < ActionController::Base
def create
post = Post.new(params[:post])
post.images.attach(params[:images])

View File

@@ -4,6 +4,8 @@ definition
| class_variables.rb:9:1:16:3 | self (X) | class_variables.rb:9:1:16:3 | self |
| class_variables.rb:10:3:12:5 | self (b) | class_variables.rb:10:3:12:5 | self |
| class_variables.rb:13:3:15:5 | self (s) | class_variables.rb:13:3:15:5 | self |
| class_variables.rb:18:1:20:3 | self (Y) | class_variables.rb:18:1:20:3 | self |
| class_variables.rb:22:1:24:3 | self (M) | class_variables.rb:22:1:24:3 | self |
| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self |
| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self |
| instance_variables.rb:1:1:44:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self |
@@ -13,9 +15,9 @@ definition
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self |
| instance_variables.rb:27:6:29:1 | <captured> | instance_variables.rb:1:1:44:4 | self |
| instance_variables.rb:27:6:29:1 | <captured> self | instance_variables.rb:1:1:44:4 | self |
| instance_variables.rb:31:1:33:3 | self (bar) | instance_variables.rb:31:1:33:3 | self |
| instance_variables.rb:32:10:32:21 | <captured> | instance_variables.rb:31:1:33:3 | self |
| instance_variables.rb:32:10:32:21 | <captured> self | instance_variables.rb:31:1:33:3 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self |
| instance_variables.rb:38:4:40:6 | self (y) | instance_variables.rb:38:4:40:6 | self |
@@ -32,8 +34,8 @@ definition
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:12:9:21:11 | self |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a |
| nested_scopes.rb:18:23:18:36 | <captured> a | nested_scopes.rb:16:29:16:29 | a |
| nested_scopes.rb:18:23:18:36 | <captured> self | nested_scopes.rb:12:9:21:11 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self |
@@ -41,7 +43,7 @@ definition
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d |
| parameters.rb:1:1:1:1 | self (parameters.rb) | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self |
@@ -49,7 +51,7 @@ definition
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas |
| parameters.rb:15:1:19:3 | self (print_map) | parameters.rb:15:1:19:3 | self |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map |
| parameters.rb:16:12:18:5 | <captured> | parameters.rb:15:1:19:3 | self |
| parameters.rb:16:12:18:5 | <captured> self | parameters.rb:15:1:19:3 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block |
@@ -76,8 +78,8 @@ definition
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b |
| parameters.rb:53:1:53:6 | ... = ... | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self |
| parameters.rb:54:9:57:3 | <captured> x | parameters.rb:53:1:53:1 | x |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y |
| parameters.rb:54:19:54:23 | ... = ... | parameters.rb:53:1:53:1 | x |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x |
@@ -86,11 +88,11 @@ definition
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b |
@@ -98,9 +100,12 @@ definition
| scopes.rb:13:11:13:11 | ... = ... | scopes.rb:13:11:13:11 | c |
| scopes.rb:13:14:13:14 | ... = ... | scopes.rb:13:14:13:14 | d |
| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 |
| scopes.rb:26:1:26:12 | self (A) | scopes.rb:26:1:26:12 | self |
| scopes.rb:27:1:27:5 | ... = ... | scopes.rb:27:1:27:1 | x |
| scopes.rb:28:1:30:3 | self (B) | scopes.rb:28:1:30:3 | self |
| scopes.rb:29:3:29:7 | ... = ... | scopes.rb:29:3:29:3 | x |
| scopes.rb:32:3:32:7 | ... = ... | scopes.rb:32:3:32:3 | x |
| scopes.rb:34:1:36:3 | self (C) | scopes.rb:34:1:36:3 | self |
| scopes.rb:35:3:35:7 | ... = ... | scopes.rb:35:3:35:3 | x |
| scopes.rb:41:1:49:3 | self (M) | scopes.rb:41:1:49:3 | self |
| scopes.rb:42:2:42:9 | ... = ... | scopes.rb:42:2:42:4 | var |
@@ -120,11 +125,11 @@ definition
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self |
| ssa.rb:26:3:28:5 | <captured> self | ssa.rb:25:1:30:3 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem |
| ssa.rb:32:1:36:3 | self (m3) | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:16:35:5 | <captured> self | ssa.rb:32:1:36:3 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 |
@@ -147,19 +152,19 @@ definition
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a |
| ssa.rb:65:3:65:15 | ... = ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a |
| ssa.rb:69:5:69:17 | ... = ... | ssa.rb:65:3:65:10 | captured |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self |
| ssa.rb:75:3:75:14 | ... = ... | ssa.rb:75:3:75:10 | captured |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:74:1:79:3 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured |
| ssa.rb:76:7:78:5 | <captured> captured | ssa.rb:75:3:75:10 | captured |
| ssa.rb:76:7:78:5 | <captured> self | ssa.rb:74:1:79:3 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self |
| ssa.rb:82:3:82:14 | ... = ... | ssa.rb:82:3:82:10 | captured |
| ssa.rb:83:7:87:5 | <captured> | ssa.rb:81:1:88:3 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:81:1:88:3 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured |
| ssa.rb:83:7:87:5 | <captured> self | ssa.rb:81:1:88:3 | self |
| ssa.rb:84:10:86:8 | <captured> captured | ssa.rb:82:3:82:10 | captured |
| ssa.rb:84:10:86:8 | <captured> self | ssa.rb:81:1:88:3 | self |
read
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -179,8 +184,8 @@ read
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
| instance_variables.rb:27:6:29:1 | <captured> | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:27:6:29:1 | <captured> self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> self | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self | instance_variables.rb:36:3:36:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:42:4:42:7 | self |
@@ -202,8 +207,8 @@ read
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self |
@@ -211,8 +216,8 @@ read
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self |
@@ -222,7 +227,7 @@ read
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:12:18:5 | <captured> | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:12:18:5 | <captured> self | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
@@ -246,8 +251,8 @@ read
| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| parameters.rb:59:1:61:3 | self (tuples_nested) | parameters.rb:59:1:61:3 | self | parameters.rb:60:3:60:23 | self |
@@ -255,19 +260,19 @@ read
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -308,10 +313,10 @@ read
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | <captured> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:16:35:5 | <captured> self | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self |
@@ -331,18 +336,18 @@ read
| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> captured | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> self | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self |
| ssa.rb:83:7:87:5 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:83:7:87:5 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:84:10:86:8 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
firstRead
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -358,8 +363,8 @@ firstRead
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
| instance_variables.rb:27:6:29:1 | <captured> | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:27:6:29:1 | <captured> self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> self | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self | instance_variables.rb:36:3:36:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:41:4:41:4 | self |
| instance_variables.rb:38:4:40:6 | self (y) | instance_variables.rb:38:4:40:6 | self | instance_variables.rb:39:6:39:7 | self |
@@ -376,15 +381,15 @@ firstRead
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self |
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self |
@@ -393,7 +398,7 @@ firstRead
| parameters.rb:7:17:7:22 | client | parameters.rb:7:17:7:22 | client | parameters.rb:11:41:11:46 | client |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:12:18:5 | <captured> | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:12:18:5 | <captured> self | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
@@ -416,7 +421,7 @@ firstRead
| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| parameters.rb:59:1:61:3 | self (tuples_nested) | parameters.rb:59:1:61:3 | self | parameters.rb:60:3:60:23 | self |
@@ -424,11 +429,11 @@ firstRead
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -451,10 +456,10 @@ firstRead
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | <captured> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:16:35:5 | <captured> self | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
@@ -472,16 +477,16 @@ firstRead
| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> captured | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> self | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self |
| ssa.rb:83:7:87:5 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:83:7:87:5 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:84:10:86:8 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
lastRead
| class_variables.rb:1:1:1:3 | self (class_variables.rb) | class_variables.rb:1:1:29:4 | self | class_variables.rb:3:1:3:5 | self |
| class_variables.rb:5:1:7:3 | self (print) | class_variables.rb:5:1:7:3 | self | class_variables.rb:6:2:6:6 | self |
@@ -498,8 +503,8 @@ lastRead
| instance_variables.rb:15:3:17:5 | self (m) | instance_variables.rb:15:3:17:5 | self | instance_variables.rb:16:5:16:6 | self |
| instance_variables.rb:20:1:25:3 | self (M) | instance_variables.rb:20:1:25:3 | self | instance_variables.rb:21:2:21:3 | self |
| instance_variables.rb:22:2:24:4 | self (n) | instance_variables.rb:22:2:24:4 | self | instance_variables.rb:23:4:23:5 | self |
| instance_variables.rb:27:6:29:1 | <captured> | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:27:6:29:1 | <captured> self | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:28:3:28:4 | self |
| instance_variables.rb:32:10:32:21 | <captured> self | instance_variables.rb:31:1:33:3 | self | instance_variables.rb:32:12:32:13 | self |
| instance_variables.rb:35:1:44:4 | self (C) | instance_variables.rb:35:1:44:4 | self | instance_variables.rb:36:3:36:4 | self |
| instance_variables.rb:37:3:43:5 | self (x) | instance_variables.rb:37:3:43:5 | self | instance_variables.rb:42:6:42:7 | self |
| instance_variables.rb:38:4:40:6 | self (y) | instance_variables.rb:38:4:40:6 | self | instance_variables.rb:39:6:39:7 | self |
@@ -516,15 +521,15 @@ lastRead
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:15:23:15:23 | a | nested_scopes.rb:16:13:16:13 | a |
| nested_scopes.rb:17:15:17:19 | ... = ... | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:15:18:15 | a |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:18:23:18:36 | <captured> | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> a | nested_scopes.rb:16:29:16:29 | a | nested_scopes.rb:18:34:18:34 | a |
| nested_scopes.rb:18:23:18:36 | <captured> self | nested_scopes.rb:12:9:21:11 | self | nested_scopes.rb:18:29:18:34 | self |
| nested_scopes.rb:22:9:24:11 | self (show_a2) | nested_scopes.rb:22:9:24:11 | self | nested_scopes.rb:23:11:23:16 | self |
| nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:22:21:22:21 | a | nested_scopes.rb:23:16:23:16 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:16:28:16 | self |
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:32:11:32:16 | self |
| nested_scopes.rb:31:11:31:16 | ... = ... | nested_scopes.rb:31:11:31:11 | a | nested_scopes.rb:32:16:32:16 | a |
| nested_scopes.rb:40:1:40:18 | ... = ... | nested_scopes.rb:40:1:40:1 | d | nested_scopes.rb:41:1:41:1 | d |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:14:1:14 | x | parameters.rb:1:14:1:14 | x | parameters.rb:3:9:3:9 | x |
| parameters.rb:2:4:2:8 | ... = ... | parameters.rb:1:18:1:18 | y | parameters.rb:4:9:4:9 | y |
| parameters.rb:7:1:13:3 | self (order_pizza) | parameters.rb:7:1:13:3 | self | parameters.rb:9:5:9:33 | self |
@@ -534,7 +539,7 @@ lastRead
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:15:17:15:19 | map | parameters.rb:15:17:15:19 | map | parameters.rb:16:3:16:5 | map |
| parameters.rb:16:12:18:5 | <captured> | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:12:18:5 | <captured> self | parameters.rb:15:1:19:3 | self | parameters.rb:17:5:17:28 | self |
| parameters.rb:16:16:16:18 | key | parameters.rb:16:16:16:18 | key | parameters.rb:17:13:17:15 | key |
| parameters.rb:16:21:16:25 | value | parameters.rb:16:21:16:25 | value | parameters.rb:17:22:17:26 | value |
| parameters.rb:21:17:21:21 | block | parameters.rb:21:17:21:21 | block | parameters.rb:22:3:22:7 | block |
@@ -556,7 +561,7 @@ lastRead
| parameters.rb:49:1:51:3 | self (tuples) | parameters.rb:49:1:51:3 | self | parameters.rb:50:3:50:18 | self |
| parameters.rb:49:13:49:13 | a | parameters.rb:49:13:49:13 | a | parameters.rb:50:11:50:11 | a |
| parameters.rb:49:15:49:15 | b | parameters.rb:49:15:49:15 | b | parameters.rb:50:16:50:16 | b |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:56:4:56:9 | self |
| parameters.rb:54:14:54:14 | y | parameters.rb:54:14:54:14 | y | parameters.rb:56:9:56:9 | y |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:55:9:55:9 | x |
| parameters.rb:59:1:61:3 | self (tuples_nested) | parameters.rb:59:1:61:3 | self | parameters.rb:60:3:60:23 | self |
@@ -564,11 +569,11 @@ lastRead
| parameters.rb:59:23:59:23 | b | parameters.rb:59:23:59:23 | b | parameters.rb:60:16:60:16 | b |
| parameters.rb:59:25:59:25 | c | parameters.rb:59:25:59:25 | c | parameters.rb:60:21:60:21 | c |
| scopes.rb:1:1:1:15 | self (scopes.rb) | scopes.rb:1:1:49:4 | self | scopes.rb:8:1:8:6 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:4:4:4:8 | ... = ... | scopes.rb:4:4:4:4 | a | scopes.rb:5:9:5:9 | a |
| scopes.rb:7:1:7:5 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:8:6:8:6 | a |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:11:4:11:9 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:12:9:12:9 | a |
| scopes.rb:13:4:13:4 | ... = ... | scopes.rb:7:1:7:1 | a | scopes.rb:14:9:14:9 | a |
| scopes.rb:13:7:13:7 | ... = ... | scopes.rb:13:7:13:7 | b | scopes.rb:15:9:15:9 | b |
@@ -592,10 +597,10 @@ lastRead
| ssa.rb:25:1:30:3 | self (m2) | ssa.rb:25:1:30:3 | self | ssa.rb:29:3:29:11 | self |
| ssa.rb:25:8:25:15 | elements | ssa.rb:25:8:25:15 | elements | ssa.rb:26:15:26:22 | elements |
| ssa.rb:26:3:28:5 | ... = ... | ssa.rb:26:7:26:10 | elem | ssa.rb:27:10:27:13 | elem |
| ssa.rb:26:3:28:5 | <captured> | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | <captured> self | ssa.rb:25:1:30:3 | self | ssa.rb:27:5:27:13 | self |
| ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 | ssa.rb:26:3:28:5 | __synth__0__1 |
| ssa.rb:26:3:28:5 | call to each | ssa.rb:26:7:26:10 | elem | ssa.rb:29:8:29:11 | elem |
| ssa.rb:33:16:35:5 | <captured> | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:16:35:5 | <captured> self | ssa.rb:32:1:36:3 | self | ssa.rb:34:5:34:10 | self |
| ssa.rb:33:20:33:20 | x | ssa.rb:33:20:33:20 | x | ssa.rb:34:10:34:10 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:41:3:41:13 | self |
| ssa.rb:40:3:40:9 | ... = ... | ssa.rb:40:3:40:4 | m3 | ssa.rb:41:8:41:9 | m3 |
@@ -613,16 +618,16 @@ lastRead
| ssa.rb:64:1:72:3 | self (m9) | ssa.rb:64:1:72:3 | self | ssa.rb:71:3:71:15 | self |
| ssa.rb:64:8:64:8 | a | ssa.rb:64:8:64:8 | a | ssa.rb:66:3:66:3 | a |
| ssa.rb:66:3:70:5 | call to times | ssa.rb:65:3:65:10 | captured | ssa.rb:71:8:71:15 | captured |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:15:66:15 | a | ssa.rb:66:15:66:15 | a | ssa.rb:67:10:67:10 | a |
| ssa.rb:74:1:79:3 | self (m10) | ssa.rb:74:1:79:3 | self | ssa.rb:76:3:78:5 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:76:7:78:5 | <captured> | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> captured | ssa.rb:75:3:75:10 | captured | ssa.rb:77:15:77:22 | captured |
| ssa.rb:76:7:78:5 | <captured> self | ssa.rb:74:1:79:3 | self | ssa.rb:77:6:77:23 | self |
| ssa.rb:81:1:88:3 | self (m11) | ssa.rb:81:1:88:3 | self | ssa.rb:83:3:87:5 | self |
| ssa.rb:83:7:87:5 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
| ssa.rb:84:10:86:8 | <captured> | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:83:7:87:5 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:84:6:86:8 | self |
| ssa.rb:84:10:86:8 | <captured> captured | ssa.rb:82:3:82:10 | captured | ssa.rb:85:15:85:22 | captured |
| ssa.rb:84:10:86:8 | <captured> self | ssa.rb:81:1:88:3 | self | ssa.rb:85:10:85:22 | self |
adjacentReads
| class_variables.rb:26:1:29:3 | self (N) | class_variables.rb:26:1:29:3 | self | class_variables.rb:27:3:27:11 | self | class_variables.rb:28:3:28:7 | self |
| instance_variables.rb:1:1:1:4 | self (instance_variables.rb) | instance_variables.rb:1:1:44:4 | self | instance_variables.rb:1:1:1:4 | self | instance_variables.rb:11:1:11:9 | self |
@@ -636,19 +641,19 @@ adjacentReads
| nested_scopes.rb:13:11:13:15 | ... = ... | nested_scopes.rb:13:11:13:11 | a | nested_scopes.rb:14:16:14:16 | a | nested_scopes.rb:15:11:15:11 | a |
| nested_scopes.rb:27:7:29:9 | self (show) | nested_scopes.rb:27:7:29:9 | self | nested_scopes.rb:28:11:28:16 | self | nested_scopes.rb:28:16:28:16 | self |
| nested_scopes.rb:30:16:30:19 | self (class << ...) | nested_scopes.rb:30:7:33:9 | self | nested_scopes.rb:30:16:30:19 | self | nested_scopes.rb:32:11:32:16 | self |
| parameters.rb:1:9:5:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:1:9:5:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:3:4:3:9 | self | parameters.rb:4:4:4:9 | self |
| parameters.rb:7:26:7:31 | pizzas | parameters.rb:7:26:7:31 | pizzas | parameters.rb:8:6:8:11 | pizzas | parameters.rb:11:14:11:19 | pizzas |
| parameters.rb:25:1:28:3 | self (opt_param) | parameters.rb:25:1:28:3 | self | parameters.rb:26:3:26:11 | self | parameters.rb:27:3:27:11 | self |
| parameters.rb:25:15:25:18 | name | parameters.rb:25:15:25:18 | name | parameters.rb:25:40:25:43 | name | parameters.rb:26:8:26:11 | name |
| parameters.rb:54:9:57:3 | <captured> | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self | parameters.rb:56:4:56:9 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:9:9:18:3 | <captured> | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a |
| parameters.rb:54:9:57:3 | <captured> self | parameters.rb:1:1:62:1 | self | parameters.rb:55:4:55:9 | self | parameters.rb:56:4:56:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:4:3:9 | self | scopes.rb:3:9:3:9 | self |
| scopes.rb:2:9:6:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:3:9:3:9 | self | scopes.rb:5:4:5:9 | self |
| scopes.rb:9:9:18:3 | <captured> a | scopes.rb:7:1:7:1 | a | scopes.rb:10:9:10:9 | a | scopes.rb:11:4:11:4 | a |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:10:4:10:9 | self | scopes.rb:12:4:12:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:12:4:12:9 | self | scopes.rb:14:4:14:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:14:4:14:9 | self | scopes.rb:15:4:15:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:15:4:15:9 | self | scopes.rb:16:4:16:9 | self |
| scopes.rb:9:9:18:3 | <captured> self | scopes.rb:1:1:49:4 | self | scopes.rb:16:4:16:9 | self | scopes.rb:17:4:17:9 | self |
| scopes.rb:13:10:13:15 | ... = ... | scopes.rb:13:10:13:15 | __synth__0__1 | scopes.rb:13:11:13:11 | __synth__0__1 | scopes.rb:13:14:13:14 | __synth__0__1 |
| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:4:13:4 | __synth__0 | scopes.rb:13:7:13:7 | __synth__0 |
| scopes.rb:13:19:13:32 | ... = ... | scopes.rb:13:4:13:32 | __synth__0 | scopes.rb:13:7:13:7 | __synth__0 | scopes.rb:13:10:13:15 | __synth__0 |
@@ -671,14 +676,14 @@ adjacentReads
| ssa.rb:19:9:19:9 | phi | ssa.rb:18:8:18:8 | x | ssa.rb:20:10:20:10 | x | ssa.rb:21:5:21:5 | x |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:3:39:9 | self | ssa.rb:39:8:39:9 | self |
| ssa.rb:38:1:42:3 | self (m4) | ssa.rb:38:1:42:3 | self | ssa.rb:39:8:39:9 | self | ssa.rb:41:3:41:13 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | ssa.rb:68:5:68:17 | self |
| ssa.rb:66:11:70:5 | <captured> | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> captured | ssa.rb:65:3:65:10 | captured | ssa.rb:68:10:68:17 | captured | ssa.rb:69:5:69:12 | captured |
| ssa.rb:66:11:70:5 | <captured> self | ssa.rb:64:1:72:3 | self | ssa.rb:67:5:67:10 | self | ssa.rb:68:5:68:17 | self |
phi
| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:1:38:3 | <uninitialized> |
| parameters.rb:37:3:37:18 | phi | parameters.rb:35:16:35:16 | b | parameters.rb:35:16:35:20 | ... = ... |
| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:1:43:3 | <uninitialized> |
| parameters.rb:42:3:42:18 | phi | parameters.rb:40:15:40:15 | e | parameters.rb:40:15:40:19 | ... = ... |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:9:57:3 | <captured> |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:9:57:3 | <captured> x |
| parameters.rb:55:4:55:9 | phi | parameters.rb:53:1:53:1 | x | parameters.rb:54:19:54:23 | ... = ... |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:6:5:6:9 | ... = ... |
| ssa.rb:5:3:13:5 | phi | ssa.rb:2:3:2:3 | i | ssa.rb:10:5:10:9 | ... = ... |

View File

@@ -0,0 +1,9 @@
name: Print unextracted entities
description: Prints all AST and Type entities that we do not extract yet. Must be run after `setup-env`
runs:
using: composite
steps:
- name: Print unextracted entities
shell: bash
run: |
bazel run //swift/extractor/print_unextracted

View File

@@ -10,6 +10,7 @@ swift_cc_binary(
visibility = ["//swift:__pkg__"],
deps = [
"//swift/extractor/infra",
"//swift/extractor/invocation",
"//swift/extractor/remapping",
"//swift/extractor/translators",
"//swift/third_party/swift-llvm-support",

View File

@@ -0,0 +1,23 @@
#pragma once
#include <swift/AST/DiagnosticConsumer.h>
namespace codeql {
inline int translateDiagnosticsKind(swift::DiagnosticKind kind) {
using Kind = swift::DiagnosticKind;
switch (kind) {
case Kind::Error:
return 1;
case Kind::Warning:
return 2;
case Kind::Note:
return 3;
case Kind::Remark:
return 4;
default:
return 0;
}
}
} // namespace codeql

View File

@@ -10,7 +10,7 @@
#include "swift/extractor/trap/TrapDomain.h"
#include "swift/extractor/infra/SwiftTagTraits.h"
#include "swift/extractor/trap/generated/TrapClasses.h"
#include "swift/extractor/infra/file/PathHash.h"
#include "swift/extractor/infra/SwiftLocationExtractor.h"
namespace codeql {
@@ -29,8 +29,7 @@ class SwiftDispatcher {
const swift::Expr*,
const swift::Pattern*,
const swift::TypeRepr*,
const swift::TypeBase*,
std::filesystem::path>;
const swift::TypeBase*>;
template <typename E>
static constexpr bool IsStorable = std::is_constructible_v<Store::Handle, const E&>;
@@ -48,10 +47,11 @@ class SwiftDispatcher {
: sourceManager{sourceManager},
trap{trap},
currentModule{currentModule},
currentPrimarySourceFile{currentPrimarySourceFile} {
currentPrimarySourceFile{currentPrimarySourceFile},
locationExtractor(trap) {
if (currentPrimarySourceFile) {
// we make sure the file is in the trap output even if the source is empty
fetchLabel(getFilePath(currentPrimarySourceFile->getFilename()));
locationExtractor.emitFile(currentPrimarySourceFile->getFilename());
}
}
@@ -325,20 +325,7 @@ class SwiftDispatcher {
void attachLocation(swift::SourceLoc start,
swift::SourceLoc end,
TrapLabel<LocatableTag> locatableLabel) {
if (!start.isValid() || !end.isValid()) {
// invalid locations seem to come from entities synthesized by the compiler
return;
}
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
DbLocation entry{{}};
entry.file = fetchLabel(file);
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
entry.id = trap.createLabel<DbLocationTag>('{', entry.file, "}:", entry.start_line, ':',
entry.start_column, ':', entry.end_line, ':',
entry.end_column);
emit(entry);
emit(LocatableLocationsTrap{locatableLabel, entry.id});
locationExtractor.attachLocation(sourceManager, start, end, locatableLabel);
}
template <typename Tag, typename... Ts>
@@ -391,12 +378,6 @@ class SwiftDispatcher {
virtual void visit(const swift::TypeRepr* typeRepr, swift::Type type) = 0;
virtual void visit(const swift::TypeBase* type) = 0;
void visit(const std::filesystem::path& file) {
auto entry = createEntry(file, file.string());
entry.name = file.string();
emit(entry);
}
const swift::SourceManager& sourceManager;
TrapDomain& trap;
Store store;
@@ -404,6 +385,7 @@ class SwiftDispatcher {
swift::ModuleDecl& currentModule;
swift::SourceFile* currentPrimarySourceFile;
std::unordered_set<swift::ModuleDecl*> encounteredModules;
SwiftLocationExtractor locationExtractor;
};
} // namespace codeql

View File

@@ -0,0 +1,59 @@
#include <swift/AST/SourceFile.h>
#include <swift/Basic/SourceManager.h>
#include "swift/extractor/trap/TrapDomain.h"
#include "swift/extractor/trap/generated/TrapEntries.h"
#include "swift/extractor/trap/generated/TrapClasses.h"
#include "swift/extractor/infra/SwiftLocationExtractor.h"
using namespace codeql;
static std::filesystem::path getFilePath(std::string_view path) {
// TODO: this needs more testing
// TODO: check canonicalization of names on a case insensitive filesystems
// TODO: make symlink resolution conditional on CODEQL_PRESERVE_SYMLINKS=true
std::error_code ec;
auto ret = std::filesystem::canonical(path, ec);
if (ec) {
std::cerr << "Cannot get real path: " << std::quoted(path) << ": " << ec.message() << "\n";
return {};
}
return ret;
}
void SwiftLocationExtractor::attachLocation(const swift::SourceManager& sourceManager,
swift::SourceLoc start,
swift::SourceLoc end,
TrapLabel<LocatableTag> locatableLabel) {
if (!start.isValid() || !end.isValid()) {
// invalid locations seem to come from entities synthesized by the compiler
return;
}
auto file = getFilePath(sourceManager.getDisplayNameForLoc(start));
DbLocation entry{{}};
entry.file = fetchFileLabel(file);
std::tie(entry.start_line, entry.start_column) = sourceManager.getLineAndColumnInBuffer(start);
std::tie(entry.end_line, entry.end_column) = sourceManager.getLineAndColumnInBuffer(end);
entry.id = trap.createLabel<DbLocationTag>('{', entry.file, "}:", entry.start_line, ':',
entry.start_column, ':', entry.end_line, ':',
entry.end_column);
trap.emit(entry);
trap.emit(LocatableLocationsTrap{locatableLabel, entry.id});
}
void SwiftLocationExtractor::emitFile(llvm::StringRef path) {
fetchFileLabel(getFilePath(path));
}
TrapLabel<FileTag> SwiftLocationExtractor::fetchFileLabel(const std::filesystem::path& file) {
if (store.count(file)) {
return store[file];
}
DbFile entry({});
entry.id = trap.createLabel<DbFileTag>(file.string());
entry.name = file.string();
trap.emit(entry);
store[file] = entry.id;
return entry.id;
}

View File

@@ -0,0 +1,31 @@
#pragma once
#include <swift/Basic/SourceManager.h>
#include <unordered_map>
#include <filesystem>
#include "swift/extractor/trap/generated/TrapEntries.h"
#include "swift/extractor/infra/file/PathHash.h"
namespace codeql {
class TrapDomain;
class SwiftLocationExtractor {
public:
explicit SwiftLocationExtractor(TrapDomain& trap) : trap(trap) {}
void attachLocation(const swift::SourceManager& sourceManager,
swift::SourceLoc start,
swift::SourceLoc end,
TrapLabel<LocatableTag> locatableLabel);
void emitFile(llvm::StringRef path);
private:
TrapLabel<FileTag> fetchFileLabel(const std::filesystem::path& file);
TrapDomain& trap;
std::unordered_map<std::filesystem::path, TrapLabel<FileTag>> store;
};
} // namespace codeql

View File

@@ -0,0 +1,11 @@
load("//swift:rules.bzl", "swift_cc_library")
swift_cc_library(
name = "invocation",
srcs = glob(["*.cpp"]),
hdrs = glob(["*.h"]),
visibility = ["//swift:__subpackages__"],
deps = [
"//swift/extractor/infra",
],
)

View File

@@ -0,0 +1,31 @@
#include "swift/extractor/invocation/SwiftDiagnosticsConsumer.h"
#include "swift/extractor/trap/generated/TrapEntries.h"
#include "swift/extractor/trap/TrapDomain.h"
#include "swift/extractor/infra/SwiftDiagnosticKind.h"
#include <swift/AST/DiagnosticEngine.h>
#include <swift/Basic/SourceManager.h>
#include <llvm/ADT/SmallString.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
using namespace codeql;
void SwiftDiagnosticsConsumer::handleDiagnostic(swift::SourceManager& sourceManager,
const swift::DiagnosticInfo& diagInfo) {
auto message = getDiagMessage(sourceManager, diagInfo);
DiagnosticsTrap diag{};
diag.id = trap.createLabel<DiagnosticsTag>();
diag.kind = translateDiagnosticsKind(diagInfo.Kind);
diag.text = message;
trap.emit(diag);
locationExtractor.attachLocation(sourceManager, diagInfo.Loc, diagInfo.Loc, diag.id);
}
std::string SwiftDiagnosticsConsumer::getDiagMessage(swift::SourceManager& sourceManager,
const swift::DiagnosticInfo& diagInfo) {
llvm::SmallString<256> text;
llvm::raw_svector_ostream out(text);
swift::DiagnosticEngine::formatDiagnosticText(out, diagInfo.FormatString, diagInfo.FormatArgs);
return text.str().str();
}

Some files were not shown because too many files have changed in this diff Show More