Compare commits

...

162 Commits

Author SHA1 Message Date
Asger F
27e1a8bd7a Ruby: cache predicates related to getConst 2022-11-07 11:18:29 +01:00
Asger F
d4b018f242 Ruby: typo: found up -> looked up 2022-11-07 09:58:00 +01:00
Asger F
25f0382fce Ruby: replace asMethod with asCallableAstNode 2022-11-07 09:38:48 +01:00
Asger F
af5a378572 Ruby: fix typo in qldoc 2022-11-07 09:20:35 +01:00
Asger F
9a38e31baa Ruby: add explicit 'this' 2022-11-07 09:20:28 +01:00
Asger F
ff20908bbd Ruby: Assignment -> AssignExpr 2022-11-07 09:20:16 +01:00
Asger F
25dd8db423 Ruby: Refactor out getAnElementWriteCall 2022-11-07 09:18:18 +01:00
Asger F
5fa49b3319 Ruby: asExpr() -> getExprNode() 2022-11-07 09:18:00 +01:00
Asger F
8b85744d3e Ruby: use lambdaCreation and handle "proc" in there 2022-11-07 09:14:55 +01:00
Asger F
4ae90e35d5 Ruby: inline transitive class-hierarchy getters 2022-11-04 08:50:33 +01:00
Asger F
472a10fd54 Ruby: direct -> immediate 2022-11-04 08:49:01 +01:00
Asger F
0f1b3486de Ruby: Use another join order for nested constant lookup 2022-11-03 10:47:39 +01:00
Asger F
a195ea942e Ruby: only drop to CFG layer for getConstantValue() 2022-11-03 10:18:31 +01:00
Asger F
cf4a3e0bbe Ruby: 'a' -> 'an' in a qldoc 2022-11-03 10:13:39 +01:00
Asger F
fe8945b5c9 Ruby: Rename getCanonicalEnclosing/Nested module
getCanonicalEnclosingModule -> getParentModule
getCanonicalNestedModule -> getNestedModule
2022-11-03 10:10:47 +01:00
Asger F
bd2a065562 Ruby: rename ConstantValue::getX -> fromX 2022-11-03 10:03:40 +01:00
Asger F
2619f3f667 Ruby: include overridden methods in getAnInstanceSelf 2022-11-01 08:32:55 +01:00
Asger F
ab4e341e65 Ruby: fix handling of namespaces with no 'self' 2022-10-31 14:05:11 +01:00
Asger F
9da5ec79c5 Ruby: Drive-by fix a QL4QL alert 2022-10-31 14:05:11 +01:00
Asger F
e549f15b1c Ruby: fix implicit 'this' 2022-10-31 14:05:11 +01:00
Asger F
056b1e8d63 Ruby: add some basic tests 2022-10-31 14:05:11 +01:00
Asger F
9be2512050 Ruby: rename one of the PostsController2 classes
These had the same name and ended up being unified
2022-10-31 13:33:41 +01:00
Asger F
b4b34cc994 Ruby: port part of ActionController model 2022-10-31 13:33:41 +01:00
Asger F
12ce46e4b1 Ruby: port part of Railties model 2022-10-31 13:33:41 +01:00
Asger F
38955d1761 Ruby: port part of the Rails model 2022-10-31 13:33:41 +01:00
Asger F
9f59b6b439 Update type-tracking test 2022-10-31 13:33:41 +01:00
Asger F
0a8f39fe96 Ruby: recover some incomplete capture flow 2022-10-31 13:33:41 +01:00
Asger F
ff02ba5965 Ruby: include SSA param input step for flowsTo 2022-10-31 13:33:41 +01:00
Asger F
017157820a Ruby: make ParameterNode extend LocalSourceNode 2022-10-31 13:33:41 +01:00
Asger F
b29ac5249e Ruby: add type-tracking inline test in global flow test 2022-10-31 13:33:41 +01:00
Asger F
4ed61c13f8 Ruby: add some captured-variable flow tests 2022-10-31 13:33:41 +01:00
Asger F
b632e21ba0 Ruby: add ConstRef 2022-10-31 13:33:41 +01:00
Asger F
06ec03de74 Ruby: add convenience-accessors for ConstantValue 2022-10-28 15:16:14 +02:00
Asger F
046e669c78 Ruby: add getAncestorExpr 2022-10-28 15:16:14 +02:00
Asger F
77d1788619 Ruby: add data flow versions of ArrayLiteral, HashLiteral, Pair 2022-10-28 15:16:14 +02:00
Asger F
2546d09fe2 Ruby: add SetterCallNode 2022-10-28 15:16:14 +02:00
Asger F
515b8366d2 Ruby: add getAnAncestor, getADescendent 2022-10-28 15:16:14 +02:00
Asger F
c8f7519cee Ruby: add Module.getNamespaceOrTopLevel 2022-10-28 15:16:14 +02:00
Asger F
1f644a9c1d Ruby: add getEnclosingToplevel 2022-10-28 15:16:14 +02:00
Asger F
436cc60138 Ruby: update some uses of getConstantValue() 2022-10-28 15:16:14 +02:00
Asger F
156964bfc9 Ruby: add getEnclosingModule and getNestedModule 2022-10-28 15:16:14 +02:00
Asger F
67772bbc43 Ruby: Accessors for attributes and elements 2022-10-28 15:16:14 +02:00
Asger F
8976ba5583 Ruby: Add CallableNode, MethodNode, and accessors 2022-10-28 15:16:13 +02:00
Asger F
ac4cac889f Ruby: add DataFlow::ModuleNode
sdf
2022-10-24 15:35:17 +02:00
Asger F
65add15416 Ruby: add getALocalUse()
This is the inverse of getALocalSource()
2022-10-24 15:35:17 +02:00
Asger F
aab1e1f5b4 Ruby: add some helpers at the AST level 2022-10-24 15:35:17 +02:00
Tony Torralba
2148e8be4d Merge pull request #10892 from atorralba/atorralba/swift/customurlschemes
Swift: Add a new Custom URL Scheme source
2022-10-24 15:33:27 +02:00
Tony Torralba
30f5fb6d83 Update expectations after merge 2022-10-24 14:24:13 +02:00
Erik Krogh Kristensen
5ff98cd80e Merge pull request #10888 from erik-krogh/glob
Ruby: add model for Dir.glob and other Dir methods
2022-10-24 14:17:37 +02:00
Asger F
bcfe4ece6f Merge pull request #10918 from asgerf/rb/constant-compound-assignment
Ruby: handle compound constant-assignment
2022-10-24 14:07:28 +02:00
Asger F
cac2e2e2e4 Merge pull request #10928 from asgerf/rb/assumed-global-const
Ruby: assume some global constants are defined
2022-10-24 14:06:34 +02:00
Paolo Tranquilli
22adf21dd3 Merge pull request #10912 from jketema/templ-func-prototype
C++: Update test result after extractor changes
2022-10-24 13:44:02 +02:00
Tamás Vajk
1d2087b92a Merge pull request #10949 from tamasvajk/kotlin-underscore-var
Kotlin: exclude Kotlin files from `java/underscore-identifier`
2022-10-24 13:32:49 +02:00
Asger F
0ffb0f6d4d Ruby: constant lookup is unaffected by blocks 2022-10-24 13:07:21 +02:00
Chris Smowton
86e99c497d Merge pull request #10930 from smowton/smowton/fix/external-property-overloads
Kotlin: give external extension properties with matching name and file distinct trap filenames
2022-10-24 11:32:37 +01:00
erik-krogh
07d90b34df use instanceof in DirPathAccess 2022-10-24 12:05:26 +02:00
Erik Krogh Kristensen
669b0c35fe fix qldoc
Co-authored-by: Nick Rolfe <nickrolfe@github.com>
2022-10-24 12:05:26 +02:00
erik-krogh
85cd7f9121 add model for Dir.glob and other Dir methods 2022-10-24 12:05:26 +02:00
Tony Torralba
f523fbc9d0 Merge branch 'main' into atorralba/swift/customurlschemes 2022-10-24 11:41:50 +02:00
Tony Torralba
3973e1ce04 Update swift/ql/test/library-tests/dataflow/dataflow/FlowConfig.qll
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2022-10-24 11:37:51 +02:00
Chris Smowton
88c4a2f6e2 Merge pull request #10936 from smowton/smowton/fix/internal-constructor-called-from-java
Kotlin: make internal constructors' trap labels consistent with the Java extractor
2022-10-24 09:37:00 +01:00
Paolo Tranquilli
90d4861b70 Merge pull request #10875 from github/redsun82/swift-codegen-doc
Swift: add infrastructure for documenting generated code
2022-10-24 10:14:57 +02:00
Tony Torralba
80f7d58fae Add missing tests for not-quite-working flow steps 2022-10-24 09:37:22 +02:00
Paolo Tranquilli
fd226c51c1 Merge pull request #10924 from github/redsun82/swift-fix-qltest-failure-reporting
Swift: add qltest tests and fix its failure reporting
2022-10-24 09:34:12 +02:00
Paolo Tranquilli
15e5faf5b6 Merge branch 'main' into templ-func-prototype 2022-10-24 09:19:46 +02:00
Tamas Vajk
d585839b7e Kotlin: exclude Kotlin files from java/underscore-identifier 2022-10-24 09:05:28 +02:00
Tamas Vajk
0192ae8331 Kotlin: Add test case for variables named underscore 2022-10-24 09:04:54 +02:00
Arthur Baars
b3855b089a Ruby: some more tests 2022-10-22 14:15:29 +02:00
Arthur Baars
ccaa12998d Ruby: desugar compound constant-assignments 2022-10-22 01:11:35 +02:00
Nick Rolfe
e5663574fe Merge pull request #10935 from github/nickrolfe/taint-step 2022-10-21 19:28:23 +01:00
Chris Smowton
00800017fd Kotlin: make internal constructors' trap labels consistent with the Java extractor
Previously we accidentally named these something like <init>$main, which is a name-mangling the Kotlin compiler applies to internal methods but not to constructors, which look to Java just like regular public constructors.
2022-10-21 16:48:37 +01:00
Nick Rolfe
9fb436e22b Ruby: add change note for localTaintStep fix 2022-10-21 16:33:29 +01:00
Nick Rolfe
269c27757d Ruby: include value-preserving flow in localTaintStep 2022-10-21 16:17:11 +01:00
Nick Rolfe
5319216c18 Ruby: add test of TaintTracking::localFlowStep 2022-10-21 16:04:04 +01:00
Jean Helie
88c6453fa6 Merge pull request #10934 from github/jhelie/add-hello-world-workflow-atm-queries
ATM: add hello world version of workflow checking queries run
2022-10-21 16:58:44 +02:00
Jean Helie
c0593c945b ATM: add hello world version of workflow checking queries run 2022-10-21 16:37:43 +02:00
Paolo Tranquilli
6bd09b1858 Merge branch 'main' into redsun82/swift-codegen-doc 2022-10-21 15:31:52 +02:00
Chris Smowton
42d6968c20 Kotlin: give external extension properties with matching name and file distinct trap filenames 2022-10-21 14:28:53 +01:00
Paolo Tranquilli
408968a417 Swift: fix swift compilation in QL tests 2022-10-21 15:20:38 +02:00
Asger F
84ae17dcbb Ruby: ensure Object is a transitive superclass 2022-10-21 15:18:59 +02:00
Chris Smowton
5e28e5a170 Merge pull request #10909 from smowton/smowton/fix/kotlin-varargs-dataflow
Kotlin: Fix varargs dataflow, and varargs default handling
2022-10-21 13:32:34 +01:00
Paolo Tranquilli
bd62f2be0e Merge branch 'main' into redsun82/swift-fix-qltest-failure-reporting 2022-10-21 14:07:19 +02:00
Paolo Tranquilli
04f6debb88 Swift: fix bazel packaging 2022-10-21 13:42:24 +02:00
Paolo Tranquilli
dbdf6ea489 Swift: fix qltest failure reporting
`qltest.sh` was not exiting with a failure when the extractor was
failing.
2022-10-21 12:54:09 +02:00
Paolo Tranquilli
cf7a5f877b Swift: add qltest.sh tests 2022-10-21 12:54:09 +02:00
Jonas Jensen
7a8c9e7644 Merge pull request #10919 from kaspersv/kaspersv/document-equiv-rel-module
QL language spec: Document built-in equivalence relation module
2022-10-21 12:47:29 +02:00
Ian Lynagh
2e6d6e1538 Merge pull request #10894 from igfoo/igfoo/psi
Kotlin: Refactor PSI handling
2022-10-21 11:43:49 +01:00
Chris Smowton
1fe9e8457f Kotlin: Fix varargs dataflow, and varargs default handling
Dataflow requires accounting for the fact that the varargs parameter isn't necessarily last in the parameter list in a couple more places. Default handling just requires that if the only null parameter is the varargs argument, and it has no default value, then no $default method is required-- the caller is expected to simply pass nothing (at QL
/ source level) or an empty array (at JVM level).
2022-10-21 11:14:41 +01:00
Kasper Svendsen
b29ed3b85a Address reviewer comments from @jbj 2022-10-21 12:08:51 +02:00
Arthur Baars
a56ed88db2 Merge pull request #10920 from github/post-release-prep/codeql-cli-2.11.2
Post-release preparation for codeql-cli-2.11.2
2022-10-21 11:58:12 +02:00
Asger F
3fd2b9ad7b Ruby: add a comment
This would have saved me some time
2022-10-21 11:44:12 +02:00
Asger F
ee7970afcb Ruby: treat String as a builtin 2022-10-21 11:44:11 +02:00
Asger F
db58e3357b Ruby: allow speculative container qname resolution 2022-10-21 11:44:11 +02:00
Kasper Svendsen
925fd2eb45 Accept reviewer reformulation
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2022-10-21 11:34:48 +02:00
Kasper Svendsen
6fe0de8a9e Accept auto-format suggestion
Co-authored-by: Anders Schack-Mulligen <aschackmull@users.noreply.github.com>
2022-10-21 11:32:37 +02:00
Kasper Svendsen
7faea53c18 QL language spec: Document built-in equivalence relation module 2022-10-21 11:03:01 +02:00
Rasmus Wriedt Larsen
8e8fb3d34f Merge pull request #10911 from RasmusWL/location-debug
Python: add `debug based on location` snippet
2022-10-21 10:59:51 +02:00
Tony Torralba
7a43bdbf05 Apply suggestions from code review
Co-authored-by: Geoffrey White <40627776+geoffw0@users.noreply.github.com>
2022-10-21 10:20:01 +02:00
Tamás Vajk
352c20b0c8 Merge pull request #10885 from tamasvajk/kotlin-const-loop-cond-fp
Kotlin: Add test case for false positive with modified captured variable
2022-10-21 10:08:02 +02:00
github-actions[bot]
be7693283b Post-release preparation for codeql-cli-2.11.2 2022-10-21 08:07:17 +00:00
Tom Hvitved
cf35299d08 Merge pull request #10910 from hvitved/ruby/call-graph-refactor
Ruby: Refactor call graph logic for singleton methods
2022-10-21 09:36:13 +02:00
Asger F
d26b0892cf Ruby: also add an AST test 2022-10-21 09:23:21 +02:00
Asger F
038bdecad7 Ruby: add test with compound assignment to a constant 2022-10-21 09:20:03 +02:00
Tamas Vajk
9d1af76c02 Add more test cases 2022-10-21 08:51:11 +02:00
Tamas Vajk
7559d3095f Revert "Kotlin: Exclude captured variables from constant loop condition check"
This reverts commit 3e476f96bd.
2022-10-21 08:38:30 +02:00
Tom Hvitved
db699ae314 Ruby: Refactor call graph logic for singleton methods 2022-10-21 07:27:41 +02:00
Chris Smowton
ac013f9d19 Merge pull request #10889 from smowton/smowton/fix/enum-entry-class-warning
Kotlin: Don't warn on extracting an enum-entry class
2022-10-20 22:08:29 +01:00
Jeroen Ketema
4b5674af32 C++: Update test result after extractor changes 2022-10-20 22:18:32 +02:00
Rasmus Wriedt Larsen
ad915e2698 Python: add debug based on location snippet 2022-10-20 21:20:24 +02:00
Geoffrey White
138643519c Merge pull request #10757 from geoffw0/sqlinject
Swift: Query for SQL injection
2022-10-20 18:55:38 +01:00
Geoffrey White
661106c1a0 Apply suggestions from code review
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
2022-10-20 17:54:40 +01:00
Chuan-kai Lin
2e9c8c759c Merge pull request #10907 from cklin/document-assume-small-delta
QL language spec: pragma[assume_small_delta]
2022-10-20 09:00:45 -07:00
Chris Smowton
f2749a8878 Don't warn on extracting an enum-entry class 2022-10-20 16:09:45 +01:00
Chuan-kai Lin
9df725901b QL language spec: pragma[assume_small_delta] 2022-10-20 07:30:02 -07:00
Asger F
8c2c28dd56 Ruby: add test showing missing superclass edge 2022-10-20 15:56:58 +02:00
Ian Lynagh
9bc0c98b8e Kotlin: Update logs test 2022-10-20 14:18:31 +01:00
Ian Lynagh
291330c7e1 Kotlin: Small code simplification 2022-10-20 12:29:48 +01:00
Paolo Tranquilli
7144383505 Swift: fix british spelling of behaviour 2022-10-20 11:43:46 +02:00
Paolo Tranquilli
f9df8a645f Swift: fix potential name conflict on schema class fields 2022-10-20 11:30:05 +02:00
Paolo Tranquilli
8813aea893 Swift: allow default class doc name to be set for properties 2022-10-20 11:23:13 +02:00
Paolo Tranquilli
c22a7e1c81 Swift: rename _DocnameModifier to _DocModifier 2022-10-20 11:05:55 +02:00
Paolo Tranquilli
37b405f134 Swift: add generated docs for predicates 2022-10-20 11:05:01 +02:00
Paolo Tranquilli
22bd10132f Swift: insert blank line between doc and desc 2022-10-20 10:49:26 +02:00
Paolo Tranquilli
b65f49bd50 Swift: document introducer_int 2022-10-20 10:46:12 +02:00
Paolo Tranquilli
7b181a2de0 Swift: change doc of Immediate property getters 2022-10-20 10:39:37 +02:00
Paolo Tranquilli
6830c2f355 Swift: enhance property docs 2022-10-20 10:35:47 +02:00
Paolo Tranquilli
9abaa5c0b3 Swift: rename doc_name with doc in properties 2022-10-20 08:59:08 +02:00
Paolo Tranquilli
492d5aec78 Swift: rename doc to description in properties 2022-10-20 08:57:41 +02:00
Geoffrey White
5b1e138300 Swift: Another qhelp edit. 2022-10-19 20:49:26 +01:00
Geoffrey White
495f744cd3 Swift: Attempt to address qhelp suggestions. 2022-10-19 20:44:27 +01:00
Geoffrey White
05d9c7b892 Swift: More 'an SQL' -> 'a SQL'. 2022-10-19 19:44:59 +01:00
Geoffrey White
83dc6d1564 Apply suggestions from code review
Co-authored-by: Ben Ahmady <32935794+subatoi@users.noreply.github.com>
2022-10-19 19:42:35 +01:00
Ian Lynagh
74a4061508 Kotlin: Refactor PSI handling
We were giving warnings about comments, when we were actually trying to
populate numlines.
2022-10-19 18:02:24 +01:00
Tony Torralba
c2a2d6b379 Fix LaunchOptionsUrlVarDecl
Update test expectations
2022-10-19 17:42:28 +02:00
Tony Torralba
e2c9240973 Add a new Custom URL Scheme source
Also adds a couple of data flow steps to model flow through `?` expressions.
2022-10-19 16:55:14 +02:00
Tamas Vajk
3e476f96bd Kotlin: Exclude captured variables from constant loop condition check 2022-10-19 15:01:17 +02:00
Tamas Vajk
0bc57410a0 Kotlin: Add FP test case for constant loop condition 2022-10-19 14:19:49 +02:00
Paolo Tranquilli
861377f650 Swift: property doc tweaks 2022-10-19 11:40:05 +02:00
Geoffrey White
027b71381a Swift: annotate all cases. 2022-10-18 16:38:02 +01:00
Paolo Tranquilli
65fd9cbf9c Swift: docname and desc examples 2022-10-18 17:05:19 +02:00
Paolo Tranquilli
35c1d311c5 Swift: add doc name override 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
8de7df9c21 Swift: add auto-generated docs for getters 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
4d87abed0e Swift: generate docname in qlgen 2022-10-18 17:04:51 +02:00
Paolo Tranquilli
5f7fa6f915 Swift: generate class docs
Python docstrings in `schema.py` are now added to the generated classes.

As an example, a docstring is added to `Expr`.
2022-10-18 17:04:51 +02:00
Paolo Tranquilli
f41fd81965 Swift: add docstring parsing 2022-10-18 16:54:26 +02:00
Geoffrey White
9767064310 Swift: Fix bug for sqlite3_prepare_v3. 2022-10-17 13:40:35 +01:00
Geoffrey White
1221cbaee7 Swift: Updated results after merge with main. 2022-10-17 13:35:46 +01:00
Geoffrey White
13018150ed Merge branch 'main' into sqlinject 2022-10-17 13:30:14 +01:00
Geoffrey White
8eccae1cdd Swift: Fix the qhelp. 2022-10-14 15:31:53 +01:00
Geoffrey White
f96e4eb87e Swift: One more go at getting the query message how ql-for-ql wants it. 2022-10-14 15:28:14 +01:00
Geoffrey White
227b10adf6 Swift: Qhelp. 2022-10-14 15:18:47 +01:00
Geoffrey White
24c6bb4c52 Swift: More modern (?) phrasing. 2022-10-14 14:41:02 +01:00
Geoffrey White
3da3a278ab Swift: Query metadata. 2022-10-14 14:31:38 +01:00
Geoffrey White
76ff593cc5 Swift: Bring it all together into a query. 2022-10-13 16:06:44 +01:00
Geoffrey White
7d78df25bf Swift: Define SQL sinks. 2022-10-13 15:50:57 +01:00
Geoffrey White
ce5631e7cb Swift: Complete the rename. 2022-10-13 15:22:36 +01:00
Geoffrey White
12cb099376 Swift: Rename to match other languages (except Java). 2022-10-13 15:21:39 +01:00
Geoffrey White
398b2a392f Swift: Add more test variants. 2022-10-13 15:13:29 +01:00
Geoffrey White
5496b11153 Swift: Update tests based on feedback. 2022-10-12 14:52:14 +01:00
Geoffrey White
4258147edf Swift: Test SQL injection via the SQLite.swift library. 2022-10-10 17:40:22 +01:00
Geoffrey White
964c92418c Swift: Test SQL injection via the C API. 2022-10-10 17:40:22 +01:00
Geoffrey White
bcab9d8e7c Swift: Add framework for SQL Injection query. 2022-10-10 17:25:08 +01:00
310 changed files with 7317 additions and 656 deletions

View File

@@ -0,0 +1,13 @@
name: ATM Check Queries Run
# This check is required, therefore we must run it on all PRs, even if only Markdown has changed.
on:
workflow_dispatch:
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: foo
run: echo "Hello world"

View File

@@ -23,12 +23,23 @@ jobs:
- uses: ./.github/actions/fetch-codeql - uses: ./.github/actions/fetch-codeql
- name: Check QL formatting - name: Check QL formatting
run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only run: find ql "(" -name "*.ql" -or -name "*.qll" ")" -print0 | xargs -0 codeql query format --check-only
qltest-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: bazelbuild/setup-bazelisk@v2
- uses: actions/setup-python@v4
with:
python-version-file: 'swift/.python-version'
- name: Test qltest.sh
run: |
bazel test //swift/tools/test/qltest
qltest: qltest:
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
os : [ubuntu-20.04, macos-latest] os: [ ubuntu-20.04, macos-latest ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: ./.github/actions/fetch-codeql - uses: ./.github/actions/fetch-codeql

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-all name: codeql/cpp-all
version: 0.4.2 version: 0.4.3-dev
groups: cpp groups: cpp
dbscheme: semmlecode.cpp.dbscheme dbscheme: semmlecode.cpp.dbscheme
extractor: cpp extractor: cpp

View File

@@ -1,5 +1,5 @@
name: codeql/cpp-queries name: codeql/cpp-queries
version: 0.4.2 version: 0.4.3-dev
groups: groups:
- cpp - cpp
- queries - queries

View File

@@ -13,6 +13,7 @@
| test.cpp:4:26:4:26 | c<<expression>> | | test.cpp:4:26:4:26 | c<<expression>> |
| test.cpp:4:26:4:26 | c<<unnamed>> | | test.cpp:4:26:4:26 | c<<unnamed>> |
| test.cpp:5:29:5:29 | e | | test.cpp:5:29:5:29 | e |
| test.cpp:6:24:6:24 | f |
| test.cpp:6:26:6:26 | (unnamed parameter 0) | | test.cpp:6:26:6:26 | (unnamed parameter 0) |
| test.cpp:6:29:6:31 | (unnamed parameter 1) | | test.cpp:6:29:6:31 | (unnamed parameter 1) |
| test.cpp:7:20:7:20 | f | | test.cpp:7:20:7:20 | f |

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-all name: codeql/csharp-solorigate-all
version: 1.3.2 version: 1.3.3-dev
groups: groups:
- csharp - csharp
- solorigate - solorigate

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-solorigate-queries name: codeql/csharp-solorigate-queries
version: 1.3.2 version: 1.3.3-dev
groups: groups:
- csharp - csharp
- solorigate - solorigate

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-all name: codeql/csharp-all
version: 0.4.2 version: 0.4.3-dev
groups: csharp groups: csharp
dbscheme: semmlecode.csharp.dbscheme dbscheme: semmlecode.csharp.dbscheme
extractor: csharp extractor: csharp

View File

@@ -1,5 +1,5 @@
name: codeql/csharp-queries name: codeql/csharp-queries
version: 0.4.2 version: 0.4.3-dev
groups: groups:
- csharp - csharp
- queries - queries

View File

@@ -274,3 +274,70 @@ reference. For more information, see ":ref:`name-resolution`."
For information about how import statements are looked up, see "`Module resolution <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#module-resolution>`__" For information about how import statements are looked up, see "`Module resolution <https://codeql.github.com/docs/ql-language-reference/ql-language-specification/#module-resolution>`__"
in the QL language specification. in the QL language specification.
Built-in modules
****************
QL defines a ``QlBuiltins`` module that is always in scope.
Currently, it defines a single parameterized sub-module
``EquivalenceRelation``, that provides an efficient abstraction for working with
(partial) equivalence relations in QL.
Equivalence relations
=====================
The built-in ``EquivalenceRelation`` module is parameterized by a type ``T`` and a
binary base relation ``base`` on ``T``. The symmetric and transitive closure of ``base``
induces a partial equivalence relation on ``T``. If every value of ``T`` appears in
``base``, then the induced relation is an equivalence relation on ``T``.
The ``EquivalenceRelation`` module exports a ``getEquivalenceClass`` predicate that
gets the equivalence class, if any, associated with a given ``T`` element by the
(partial) equivalence relation induced by ``base``.
The following example illustrates an application of the ``EquivalenceRelation``
module to generate a custom equivalence relation:
.. code-block:: ql
class Node extends int {
Node() { this in [1 .. 6] }
}
predicate base(Node x, Node y) {
x = 1 and y = 2
or
x = 3 and y = 4
}
module Equiv = QlBuiltins::EquivalenceRelation<Node, base/2>;
from int x, int y
where Equiv::getEquivalenceClass(x) = Equiv::getEquivalenceClass(y)
select x, y
Since ``base`` does not relate ``5`` or ``6`` to any nodes, the induced
relation is a partial equivalence relation on ``Node`` and does not relate ``5``
or ``6`` to any nodes either.
The above select clause returns the following partial equivalence relation:
+---+---+
| x | y |
+===+===+
| 1 | 1 |
+---+---+
| 1 | 2 |
+---+---+
| 2 | 1 |
+---+---+
| 2 | 2 |
+---+---+
| 3 | 3 |
+---+---+
| 3 | 4 |
+---+---+
| 4 | 3 |
+---+---+
| 4 | 4 |
+---+---+

View File

@@ -639,7 +639,7 @@ Various kinds of syntax can have *annotations* applied to them. Annotations are
| "override" | "override"
| "query" | "query"
argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt") "]" argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
| "language" "[" "monotonicAggregates" "]" | "language" "[" "monotonicAggregates" "]"
| "bindingset" "[" (variable ( "," variable)*)? "]" | "bindingset" "[" (variable ( "," variable)*)? "]"
@@ -687,17 +687,19 @@ Parameterized annotations take some additional arguments.
The parameterized annotation ``pragma`` supplies compiler pragmas, and may be applied in various contexts depending on the pragma in question. The parameterized annotation ``pragma`` supplies compiler pragmas, and may be applied in various contexts depending on the pragma in question.
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+ +---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases | | Pragma | Classes | Characters | Member predicates | Non-member predicates | Imports | Fields | Modules | Aliases |
+==============+=========+============+===================+=======================+=========+========+=========+=========+ +===========================+=========+============+===================+=======================+=========+========+=========+=========+
| ``inline`` | | yes | yes | yes | | | | | | ``inline`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+ +---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noinline`` | | yes | yes | yes | | | | | | ``noinline`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+ +---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``nomagic`` | | yes | yes | yes | | | | | | ``nomagic`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+ +---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``noopt`` | | yes | yes | yes | | | | | | ``noopt`` | | yes | yes | yes | | | | |
+--------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+ +---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
| ``assume_small_delta`` | | yes | yes | yes | | | | |
+---------------------------+---------+------------+-------------------+-----------------------+---------+--------+---------+---------+
The parameterized annotation ``language`` supplies language pragmas which change the behavior of the language. Language pragmas apply at the scope level, and are inherited by nested scopes. The parameterized annotation ``language`` supplies language pragmas which change the behavior of the language. Language pragmas apply at the scope level, and are inherited by nested scopes.
@@ -2048,7 +2050,7 @@ The complete grammar for QL is as follows:
| "override" | "override"
| "query" | "query"
argsAnnotation ::= "pragma" "[" ("noinline" | "nomagic" | "noopt") "]" argsAnnotation ::= "pragma" "[" ("inline" | "noinline" | "nomagic" | "noopt" | "assume_small_delta") "]"
| "language" "[" "monotonicAggregates" "]" | "language" "[" "monotonicAggregates" "]"
| "bindingset" "[" (variable ( "," variable)*)? "]" | "bindingset" "[" (variable ( "," variable)*)? "]"

View File

@@ -1,5 +1,5 @@
name: codeql/go-all name: codeql/go-all
version: 0.3.2 version: 0.3.3-dev
groups: go groups: go
dbscheme: go.dbscheme dbscheme: go.dbscheme
extractor: go extractor: go

View File

@@ -1,5 +1,5 @@
name: codeql/go-queries name: codeql/go-queries
version: 0.3.2 version: 0.3.3-dev
groups: groups:
- go - go
- queries - queries

View File

@@ -35,8 +35,6 @@ class ExternalDeclExtractor(val logger: FileLogger, val invocationTrapFile: Stri
if (ret) externalDeclWorkList.add(Pair(d, signature)) if (ret) externalDeclWorkList.add(Pair(d, signature))
return ret return ret
} }
fun extractLater(p: IrProperty) = extractLater(p, propertySignature)
fun extractLater(f: IrField) = extractLater(f, fieldSignature)
fun extractLater(c: IrClass) = extractLater(c, "") fun extractLater(c: IrClass) = extractLater(c, "")
fun extractExternalClasses() { fun extractExternalClasses() {

View File

@@ -2,6 +2,7 @@ package com.github.codeql
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -139,6 +140,8 @@ class KotlinExtractorExtension(
logger.flush() logger.flush()
logger.info("Extraction for invocation TRAP file $invocationTrapFile") logger.info("Extraction for invocation TRAP file $invocationTrapFile")
logger.flush() logger.flush()
logger.info("Kotlin version ${KotlinCompilerVersion.getVersion()}")
logger.flush()
logPeakMemoryUsage(logger, "before extractor") logPeakMemoryUsage(logger, "before extractor")
if (System.getenv("CODEQL_EXTRACTOR_JAVA_KOTLIN_DUMP") == "true") { if (System.getenv("CODEQL_EXTRACTOR_JAVA_KOTLIN_DUMP") == "true") {
logger.info("moduleFragment:\n" + moduleFragment.dump()) logger.info("moduleFragment:\n" + moduleFragment.dump())

View File

@@ -305,7 +305,7 @@ open class KotlinFileExtractor(
val kind = c.kind val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) { if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId) tw.writeIsEnumType(classId)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) { } else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.errorElement("Unrecognised class kind $kind", c) logger.errorElement("Unrecognised class kind $kind", c)
} }
} }
@@ -452,7 +452,7 @@ open class KotlinFileExtractor(
val kind = c.kind val kind = c.kind
if (kind == ClassKind.ENUM_CLASS) { if (kind == ClassKind.ENUM_CLASS) {
tw.writeIsEnumType(classId) tw.writeIsEnumType(classId)
} else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT) { } else if (kind != ClassKind.CLASS && kind != ClassKind.OBJECT && kind != ClassKind.ENUM_ENTRY) {
logger.warnElement("Unrecognised class kind $kind", c) logger.warnElement("Unrecognised class kind $kind", c)
} }
@@ -1884,7 +1884,7 @@ open class KotlinFileExtractor(
IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget)) IrConstImpl.defaultValueForType(0, 0, getDefaultsMethodLastArgType(callTarget))
) )
extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx) extractCallValueArguments(id, valueArgsWithDummies + extraArgs, enclosingStmt, enclosingCallable, nextIdx, extractVarargAsArray = true)
} }
private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? { private fun getFunctionInvokeMethod(typeArgs: List<IrTypeArgument>): IrFunction? {
@@ -1961,8 +1961,12 @@ open class KotlinFileExtractor(
superQualifierSymbol: IrClassSymbol? = null) { superQualifierSymbol: IrClassSymbol? = null) {
val locId = tw.getLocation(locElement) val locId = tw.getLocation(locElement)
val varargParam = syntacticCallTarget.valueParameters.withIndex().find { it.value.isVararg }
// If the vararg param is the only one not specified, and it has no default value, then we don't need to call a $default method,
// as omitting it already implies passing an empty vararg array.
val nullAllowedIdx = if (varargParam != null && varargParam.value.defaultValue == null) varargParam.index else -1
if (valueArguments.any { it == null }) { if (valueArguments.withIndex().any { (index, it) -> it == null && index != nullAllowedIdx }) {
extractsDefaultsCall( extractsDefaultsCall(
syntacticCallTarget, syntacticCallTarget,
locId, locId,
@@ -2082,11 +2086,11 @@ open class KotlinFileExtractor(
private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) = private fun extractCallValueArguments(callId: Label<out DbExprparent>, call: IrFunctionAccessExpression, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) =
extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset) extractCallValueArguments(callId, (0 until call.valueArgumentsCount).map { call.getValueArgument(it) }, enclosingStmt, enclosingCallable, idxOffset)
private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int) { private fun extractCallValueArguments(callId: Label<out DbExprparent>, valueArguments: List<IrExpression?>, enclosingStmt: Label<out DbStmt>, enclosingCallable: Label<out DbCallable>, idxOffset: Int, extractVarargAsArray: Boolean = false) {
var i = 0 var i = 0
valueArguments.forEach { arg -> valueArguments.forEach { arg ->
if(arg != null) { if(arg != null) {
if (arg is IrVararg) { if (arg is IrVararg && !extractVarargAsArray) {
arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) } arg.elements.forEachIndexed { varargNo, vararg -> extractVarargElement(vararg, enclosingCallable, callId, i + idxOffset + varargNo, enclosingStmt) }
i += arg.elements.size i += arg.elements.size
} else { } else {

View File

@@ -253,19 +253,24 @@ open class KotlinUsesExtractor(
} }
} }
private fun propertySignature(p: IrProperty) =
((p.getter ?: p.setter)?.extensionReceiverParameter?.let { useType(erase(it.type)).javaResult.signature } ?: "")
private fun extractPropertyLaterIfExternalFileMember(p: IrProperty) { private fun extractPropertyLaterIfExternalFileMember(p: IrProperty) {
if (isExternalFileClassMember(p)) { if (isExternalFileClassMember(p)) {
extractExternalClassLater(p.parentAsClass) extractExternalClassLater(p.parentAsClass)
dependencyCollector?.addDependency(p, externalClassExtractor.propertySignature) val signature = propertySignature(p) + externalClassExtractor.propertySignature
externalClassExtractor.extractLater(p) dependencyCollector?.addDependency(p, signature)
externalClassExtractor.extractLater(p, signature)
} }
} }
private fun extractFieldLaterIfExternalFileMember(f: IrField) { private fun extractFieldLaterIfExternalFileMember(f: IrField) {
if (isExternalFileClassMember(f)) { if (isExternalFileClassMember(f)) {
extractExternalClassLater(f.parentAsClass) extractExternalClassLater(f.parentAsClass)
dependencyCollector?.addDependency(f, externalClassExtractor.fieldSignature) val signature = (f.correspondingPropertySymbol?.let { propertySignature(it.owner) } ?: "") + externalClassExtractor.fieldSignature
externalClassExtractor.extractLater(f) dependencyCollector?.addDependency(f, signature)
externalClassExtractor.extractLater(f, signature)
} }
} }
@@ -813,7 +818,7 @@ open class KotlinUsesExtractor(
OperatorNameConventions.INVOKE.asString()) OperatorNameConventions.INVOKE.asString())
fun getSuffixIfInternal() = fun getSuffixIfInternal() =
if (f.visibility == DescriptorVisibilities.INTERNAL) { if (f.visibility == DescriptorVisibilities.INTERNAL && f !is IrConstructor) {
"\$" + getJvmModuleName(f) "\$" + getJvmModuleName(f)
} else { } else {
"" ""

View File

@@ -1,9 +1,10 @@
package com.github.codeql package com.github.codeql
import com.github.codeql.utils.versions.Psi2Ir import com.github.codeql.utils.versions.getPsi2Ir
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiWhiteSpace import com.intellij.psi.PsiWhiteSpace
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.kdoc.psi.api.KDocElement import org.jetbrains.kotlin.kdoc.psi.api.KDocElement
@@ -15,9 +16,16 @@ class LinesOfCode(
val tw: FileTrapWriter, val tw: FileTrapWriter,
val file: IrFile val file: IrFile
) { ) {
val psi2Ir = Psi2Ir(logger) val psi2Ir = getPsi2Ir(logger).also {
if (it == null) {
logger.warn("Lines of code will not be populated as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
}
}
fun linesOfCodeInFile(id: Label<DbFile>) { fun linesOfCodeInFile(id: Label<DbFile>) {
if (psi2Ir == null) {
return
}
val ktFile = psi2Ir.getKtFile(file) val ktFile = psi2Ir.getKtFile(file)
if (ktFile == null) { if (ktFile == null) {
return return
@@ -26,6 +34,9 @@ class LinesOfCode(
} }
fun linesOfCodeInDeclaration(d: IrDeclaration, id: Label<out DbSourceline>) { fun linesOfCodeInDeclaration(d: IrDeclaration, id: Label<out DbSourceline>) {
if (psi2Ir == null) {
return
}
val p = psi2Ir.findPsiElement(d, file) val p = psi2Ir.findPsiElement(d, file)
if (p == null) { if (p == null) {
return return

View File

@@ -3,9 +3,11 @@ package com.github.codeql.comments
import com.github.codeql.* import com.github.codeql.*
import com.github.codeql.utils.IrVisitorLookup import com.github.codeql.utils.IrVisitorLookup
import com.github.codeql.utils.isLocalFunction import com.github.codeql.utils.isLocalFunction
import com.github.codeql.utils.versions.Psi2Ir import com.github.codeql.utils.versions.getPsi2Ir
import com.github.codeql.utils.versions.Psi2IrFacade
import com.intellij.psi.PsiComment import com.intellij.psi.PsiComment
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrBody
@@ -21,18 +23,23 @@ import org.jetbrains.kotlin.psi.psiUtil.startOffset
class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label<out DbFile>) { class CommentExtractor(private val fileExtractor: KotlinFileExtractor, private val file: IrFile, private val fileLabel: Label<out DbFile>) {
private val tw = fileExtractor.tw private val tw = fileExtractor.tw
private val logger = fileExtractor.logger private val logger = fileExtractor.logger
private val psi2Ir = Psi2Ir(logger)
private val ktFile = psi2Ir.getKtFile(file)
fun extract() { fun extract() {
val psi2Ir = getPsi2Ir(logger)
if (psi2Ir == null) {
logger.warn("Comments will not be extracted as Kotlin version is too old (${KotlinCompilerVersion.getVersion()})")
return
}
val ktFile = psi2Ir.getKtFile(file)
if (ktFile == null) { if (ktFile == null) {
logger.warn("Comments are not being processed in ${file.path}.") logger.warn("Comments are not being processed in ${file.path}.")
} else { return
ktFile.accept(commentVisitor)
} }
val commentVisitor = mkCommentVisitor(psi2Ir)
ktFile.accept(commentVisitor)
} }
private val commentVisitor = private fun mkCommentVisitor(psi2Ir: Psi2IrFacade): KtVisitor<Unit, Unit> =
object : KtVisitor<Unit, Unit>() { object : KtVisitor<Unit, Unit>() {
override fun visitElement(element: PsiElement) { override fun visitElement(element: PsiElement) {
element.acceptChildren(this) element.acceptChildren(this)

View File

@@ -1,6 +1,6 @@
package com.github.codeql.utils package com.github.codeql.utils
import com.github.codeql.utils.versions.Psi2Ir import com.github.codeql.utils.versions.Psi2IrFacade
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrDeclaration
@@ -8,7 +8,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.isFakeOverride import org.jetbrains.kotlin.ir.util.isFakeOverride
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
class IrVisitorLookup(private val psi2Ir: Psi2Ir, private val psi: PsiElement, private val file: IrFile) : class IrVisitorLookup(private val psi2Ir: Psi2IrFacade, private val psi: PsiElement, private val file: IrFile) :
IrElementVisitor<Unit, MutableCollection<IrElement>> { IrElementVisitor<Unit, MutableCollection<IrElement>> {
private val location = psi.getLocation() private val location = psi.getLocation()

View File

@@ -1,19 +1,5 @@
package com.github.codeql.utils.versions package com.github.codeql.utils.versions
import com.github.codeql.FileLogger import com.github.codeql.FileLogger
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.psi.KtFile
class Psi2Ir(private val logger: FileLogger) : Psi2IrFacade { fun getPsi2Ir(@Suppress("UNUSED_PARAMETER") logger: FileLogger): Psi2IrFacade? = null
override fun getKtFile(irFile: IrFile): KtFile? {
logger.warn("Comment extraction is not supported for Kotlin < 1.5.20")
return null
}
override fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
logger.error("Attempted comment extraction for Kotlin < 1.5.20")
return null
}
}

View File

@@ -8,7 +8,9 @@ import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
class Psi2Ir(private val logger: FileLogger): Psi2IrFacade { fun getPsi2Ir(logger: FileLogger): Psi2IrFacade? = Psi2Ir(logger)
private class Psi2Ir(private val logger: FileLogger): Psi2IrFacade {
override fun getKtFile(irFile: IrFile): KtFile? { override fun getKtFile(irFile: IrFile): KtFile? {
return irFile.getKtFile() return irFile.getKtFile()
} }

View File

@@ -0,0 +1,2 @@
| user.kt:3:22:3:22 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |
| user.kt:3:28:3:28 | getF(...) | lib/lib/TestKt.class:0:0:0:0 | getF |

View File

@@ -0,0 +1,7 @@
package lib
val String.f
get() = 1
val Int.f
get() = 2

View File

@@ -0,0 +1,4 @@
from create_database_utils import *
runSuccessfully(["kotlinc", "test.kt", "-d", "lib"])
run_codeql_database_create(["kotlinc user.kt -cp lib"], lang="java")

View File

@@ -0,0 +1,4 @@
import java
from MethodAccess ma
select ma, ma.getCallee()

View File

@@ -0,0 +1,3 @@
import lib.f
fun test() = "hello".f + 1.f

View File

@@ -32,6 +32,7 @@ with open('logs.csv', 'w', newline='') as f_out:
j = json.loads(line) j = json.loads(line)
msg = j['message'] msg = j['message']
msg = re.sub('(?<=Extraction for invocation TRAP file ).*/kt-db/trap/java/invocations/kotlin\..*\.trap', '<FILENAME>', msg) msg = re.sub('(?<=Extraction for invocation TRAP file ).*/kt-db/trap/java/invocations/kotlin\..*\.trap', '<FILENAME>', msg)
msg = re.sub('(?<=Kotlin version )[0-9.]+', '<VERSION>', msg)
if msg.startswith('Peak memory: '): if msg.startswith('Peak memory: '):
# Peak memory information varies from run to run, so just ignore it # Peak memory information varies from run to run, so just ignore it
continue continue

View File

@@ -1,5 +1,6 @@
| 1 | 1 | Test script | Log file | 1 | | 1 | 1 | Test script | Log file | 1 |
| 1 | 2 | CodeQL Kotlin extractor | INFO | Extraction started | | 1 | 2 | CodeQL Kotlin extractor | INFO | Extraction started |
| 1 | 3 | CodeQL Kotlin extractor | INFO | Extraction for invocation TRAP file <FILENAME> | | 1 | 3 | CodeQL Kotlin extractor | INFO | Extraction for invocation TRAP file <FILENAME> |
| 1 | 4 | CodeQL Kotlin extractor | INFO | Extracting file test.kt | | 1 | 4 | CodeQL Kotlin extractor | INFO | Kotlin version <VERSION> |
| 1 | 5 | CodeQL Kotlin extractor | INFO | Extraction completed | | 1 | 5 | CodeQL Kotlin extractor | INFO | Extracting file test.kt |
| 1 | 6 | CodeQL Kotlin extractor | INFO | Extraction completed |

View File

@@ -1,5 +1,5 @@
name: codeql/java-all name: codeql/java-all
version: 0.4.2 version: 0.4.3-dev
groups: java groups: java
dbscheme: config/semmlecode.dbscheme dbscheme: config/semmlecode.dbscheme
extractor: java extractor: java

View File

@@ -2367,14 +2367,33 @@ class Argument extends Expr {
*/ */
predicate isNthVararg(int arrayindex) { predicate isNthVararg(int arrayindex) {
not this.isExplicitVarargsArray() and not this.isExplicitVarargsArray() and
exists(Callable tgt, int varargsParamPos | exists(Callable tgt |
call.getCallee() = tgt and call.getCallee() = tgt and
tgt.getParameter(varargsParamPos).isVarargs() and arrayindex = pos - tgt.getVaragsParameterIndex() and
arrayindex = pos - varargsParamPos and
arrayindex >= 0 and arrayindex >= 0 and
arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters() arrayindex <= call.getNumArgument() - tgt.getNumberOfParameters()
) )
} }
/**
* Gets the parameter position that will receive this argument.
*
* For all vararg arguments, this is the position of the vararg array parameter.
*/
int getParameterPos() {
exists(Callable c | c = call.getCallee() |
if c.isVarargs()
then
if pos < c.getVaragsParameterIndex()
then result = pos // Vararg method argument, before the vararg parameter
else (
if this.isVararg()
then result = c.getVaragsParameterIndex() // Part of the implicit vararg array
else result = pos - (call.getNumArgument() - c.getNumberOfParameters()) // Vararg method argument, after the vararg parameter (offset could be -1 in the zero-vararg case)
)
else result = pos // Not a vararg method
)
}
} }
/** /**

View File

@@ -283,6 +283,9 @@ class Callable extends StmtParent, Member, @callable {
/** Holds if the last parameter of this callable is a varargs (variable arity) parameter. */ /** Holds if the last parameter of this callable is a varargs (variable arity) parameter. */
predicate isVarargs() { this.getAParameter().isVarargs() } predicate isVarargs() { this.getAParameter().isVarargs() }
/** Gets the index of this callable's varargs parameter, if any exists. */
int getVaragsParameterIndex() { this.getParameter(result).isVarargs() }
/** /**
* Gets the signature of this callable, where all types in the signature have a fully-qualified name. * Gets the signature of this callable, where all types in the signature have a fully-qualified name.
* The parameter types are only separated by a comma (without space). If this callable has * The parameter types are only separated by a comma (without space). If this callable has

View File

@@ -378,11 +378,11 @@ module Private {
*/ */
predicate argumentOf(DataFlowCall call, int pos) { predicate argumentOf(DataFlowCall call, int pos) {
exists(Argument arg | this.asExpr() = arg | exists(Argument arg | this.asExpr() = arg |
call.asCall() = arg.getCall() and pos = arg.getPosition() call.asCall() = arg.getCall() and pos = arg.getParameterPos()
) )
or or
call.asCall() = this.(ImplicitVarargsArray).getCall() and call.asCall() = this.(ImplicitVarargsArray).getCall() and
pos = call.asCall().getCallee().getNumberOfParameters() - 1 pos = call.asCall().getCallee().getVaragsParameterIndex()
or or
pos = -1 and this = getInstanceArgument(call.asCall()) pos = -1 and this = getInstanceArgument(call.asCall())
or or

View File

@@ -282,13 +282,8 @@ private predicate constructorStep(Expr tracked, ConstructorCall sink) {
* Converts an argument index to a formal parameter index. * Converts an argument index to a formal parameter index.
* This is relevant for varadic methods. * This is relevant for varadic methods.
*/ */
private int argToParam(Call call, int arg) { private int argToParam(Call call, int argIdx) {
exists(call.getArgument(arg)) and result = call.getArgument(argIdx).(Argument).getParameterPos()
exists(Callable c | c = call.getCallee() |
if c.isVarargs() and arg >= c.getNumberOfParameters()
then result = c.getNumberOfParameters() - 1
else result = arg
)
} }
/** Access to a method that passes taint from qualifier to argument. */ /** Access to a method that passes taint from qualifier to argument. */

View File

@@ -22,7 +22,7 @@ class IdentifierElement extends Element {
from IdentifierElement e, string msg from IdentifierElement e, string msg
where where
e.fromSource() and e.getCompilationUnit().isJavaSourceFile() and
not e.(Constructor).isDefaultConstructor() and not e.(Constructor).isDefaultConstructor() and
( (
e.getName() = "_" and e.getName() = "_" and

View File

@@ -1,5 +1,5 @@
name: codeql/java-queries name: codeql/java-queries
version: 0.4.2 version: 0.4.3-dev
groups: groups:
- java - java
- queries - queries

View File

@@ -0,0 +1,5 @@
public class User {
public static void test() { new Test(1, 2); }
}

View File

@@ -0,0 +1 @@
| User.java:3:31:3:44 | new Test(...) | test.kt:3:3:3:51 | { ... } |

View File

@@ -0,0 +1,5 @@
public class Test() {
internal constructor(x: Int, y: Int) : this() { }
}

View File

@@ -0,0 +1,4 @@
import java
from ClassInstanceExpr ce
select ce, ce.getConstructor().getBody()

View File

@@ -0,0 +1,4 @@
import java
import semmle.code.java.Diagnostics
select any(Diagnostic d | not d.toString().matches("Not rewriting trap file for%"))

View File

@@ -7,6 +7,394 @@ test.kt:
# 1| 0: [Parameter] a # 1| 0: [Parameter] a
# 1| 0: [TypeAccess] Object # 1| 0: [TypeAccess] Object
# 1| 5: [BlockStmt] { ... } # 1| 5: [BlockStmt] { ... }
# 184| 2: [Method] varargsTest
# 184| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 184| 0: [Parameter] x
# 184| 0: [TypeAccess] String
# 184| 1: [Parameter] y
# 184| 0: [TypeAccess] String[]
# 184| 0: [WildcardTypeAccess] ? ...
# 184| 0: [TypeAccess] String
# 184| 2: [Parameter] z
# 184| 0: [TypeAccess] String
# 184| 5: [BlockStmt] { ... }
# 185| 0: [ExprStmt] <Expr>;
# 185| 0: [MethodAccess] sink(...)
# 185| -1: [TypeAccess] TestKt
# 185| 0: [VarAccess] x
# 186| 1: [ExprStmt] <Expr>;
# 186| 0: [MethodAccess] sink(...)
# 186| -1: [TypeAccess] TestKt
# 186| 0: [ArrayAccess] ...[...]
# 186| 0: [VarAccess] y
# 186| 1: [IntegerLiteral] 0
# 187| 2: [ExprStmt] <Expr>;
# 187| 0: [MethodAccess] sink(...)
# 187| -1: [TypeAccess] TestKt
# 187| 0: [VarAccess] z
# 184| 3: [Method] varargsTest$default
# 184| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 184| 0: [Parameter] p0
# 184| 0: [TypeAccess] String
# 184| 1: [Parameter] p1
# 184| 0: [TypeAccess] String[]
# 184| 2: [Parameter] p2
# 184| 0: [TypeAccess] String
# 184| 3: [Parameter] p3
# 184| 0: [TypeAccess] int
# 184| 4: [Parameter] p4
# 184| 0: [TypeAccess] Object
# 184| 5: [BlockStmt] { ... }
# 184| 0: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 1
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p0
# 184| 1: [StringLiteral] before-vararg-default sunk
# 184| 1: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 2
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p1
# 184| 1: [ArrayCreationExpr] new String[]
# 184| -2: [ArrayInit] {...}
# 184| 0: [StringLiteral] first-vararg-default sunk
# 184| 1: [StringLiteral] second-vararg-default sunk
# 184| -1: [TypeAccess] String
# 184| 0: [IntegerLiteral] 2
# 184| 2: [IfStmt] if (...)
# 184| 0: [EQExpr] ... == ...
# 184| 0: [AndBitwiseExpr] ... & ...
# 184| 0: [IntegerLiteral] 4
# 184| 1: [VarAccess] p3
# 184| 1: [IntegerLiteral] 0
# 184| 1: [ExprStmt] <Expr>;
# 184| 0: [AssignExpr] ...=...
# 184| 0: [VarAccess] p2
# 184| 1: [StringLiteral] after-vararg-default sunk
# 184| 3: [ReturnStmt] return ...
# 184| 0: [MethodAccess] varargsTest(...)
# 184| -1: [TypeAccess] TestKt
# 184| 0: [VarAccess] p0
# 184| 1: [VarAccess] p1
# 184| 2: [VarAccess] p2
# 190| 4: [Method] varargsUser
# 190| 3: [TypeAccess] Unit
# 190| 5: [BlockStmt] { ... }
# 191| 0: [ExprStmt] <Expr>;
# 191| 0: [MethodAccess] varargsTest$default(...)
# 191| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 192| 1: [ExprStmt] <Expr>;
# 192| 0: [MethodAccess] varargsTest$default(...)
# 192| -1: [TypeAccess] TestKt
# 192| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 193| 2: [ExprStmt] <Expr>;
# 193| 0: [MethodAccess] varargsTest$default(...)
# 193| -1: [TypeAccess] TestKt
# 193| 0: [StringLiteral] no-varargs-before sunk
# 1| 1: [NullLiteral] null
# 193| 2: [StringLiteral] no-varargs-after sunk
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 194| 3: [ExprStmt] <Expr>;
# 194| 0: [MethodAccess] varargsTest(...)
# 194| -1: [TypeAccess] TestKt
# 194| 0: [StringLiteral] one-vararg-before sunk
# 194| 1: [StringLiteral] one-vararg sunk
# 194| 2: [StringLiteral] one-vararg-after sunk
# 195| 4: [ExprStmt] <Expr>;
# 195| 0: [MethodAccess] varargsTest(...)
# 195| -1: [TypeAccess] TestKt
# 195| 0: [StringLiteral] two-varargs-before sunk
# 195| 1: [StringLiteral] two-vararg-first sunk
# 195| 2: [StringLiteral] two-vararg-second sunk
# 195| 3: [StringLiteral] two-varargs-after sunk
# 196| 5: [ExprStmt] <Expr>;
# 196| 0: [MethodAccess] varargsTest$default(...)
# 196| -1: [TypeAccess] TestKt
# 196| 0: [StringLiteral] no-z-parmeter sunk
# 196| 1: [ArrayCreationExpr] new String[]
# 196| -2: [ArrayInit] {...}
# 196| 0: [StringLiteral] no-z-parameter first vararg sunk
# 196| 1: [StringLiteral] no-z-parameter second vararg sunk
# 196| -1: [TypeAccess] String
# 196| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 199| 5: [Method] varargsTestOnlySinkVarargs
# 199| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 199| 0: [Parameter] x
# 199| 0: [TypeAccess] String
# 199| 1: [Parameter] y
# 199| 0: [TypeAccess] String[]
# 199| 0: [WildcardTypeAccess] ? ...
# 199| 0: [TypeAccess] String
# 199| 2: [Parameter] z
# 199| 0: [TypeAccess] String
# 199| 5: [BlockStmt] { ... }
# 200| 0: [ExprStmt] <Expr>;
# 200| 0: [MethodAccess] sink(...)
# 200| -1: [TypeAccess] TestKt
# 200| 0: [ArrayAccess] ...[...]
# 200| 0: [VarAccess] y
# 200| 1: [IntegerLiteral] 0
# 199| 6: [Method] varargsTestOnlySinkVarargs$default
# 199| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 199| 0: [Parameter] p0
# 199| 0: [TypeAccess] String
# 199| 1: [Parameter] p1
# 199| 0: [TypeAccess] String[]
# 199| 2: [Parameter] p2
# 199| 0: [TypeAccess] String
# 199| 3: [Parameter] p3
# 199| 0: [TypeAccess] int
# 199| 4: [Parameter] p4
# 199| 0: [TypeAccess] Object
# 199| 5: [BlockStmt] { ... }
# 199| 0: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 1
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p0
# 199| 1: [StringLiteral] before-vararg-default not sunk 2
# 199| 1: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 2
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p1
# 199| 1: [ArrayCreationExpr] new String[]
# 199| -2: [ArrayInit] {...}
# 199| 0: [StringLiteral] first-vararg-default sunk 2
# 199| 1: [StringLiteral] second-vararg-default sunk 2
# 199| -1: [TypeAccess] String
# 199| 0: [IntegerLiteral] 2
# 199| 2: [IfStmt] if (...)
# 199| 0: [EQExpr] ... == ...
# 199| 0: [AndBitwiseExpr] ... & ...
# 199| 0: [IntegerLiteral] 4
# 199| 1: [VarAccess] p3
# 199| 1: [IntegerLiteral] 0
# 199| 1: [ExprStmt] <Expr>;
# 199| 0: [AssignExpr] ...=...
# 199| 0: [VarAccess] p2
# 199| 1: [StringLiteral] after-vararg-default not sunk 2
# 199| 3: [ReturnStmt] return ...
# 199| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 199| -1: [TypeAccess] TestKt
# 199| 0: [VarAccess] p0
# 199| 1: [VarAccess] p1
# 199| 2: [VarAccess] p2
# 203| 7: [Method] varargsUserOnlySinkVarargs
# 203| 3: [TypeAccess] Unit
# 203| 5: [BlockStmt] { ... }
# 204| 0: [ExprStmt] <Expr>;
# 204| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 204| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 205| 1: [ExprStmt] <Expr>;
# 205| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 205| -1: [TypeAccess] TestKt
# 205| 0: [StringLiteral] no-varargs-before, no-z-parameter not sunk 2
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 206| 2: [ExprStmt] <Expr>;
# 206| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 206| -1: [TypeAccess] TestKt
# 206| 0: [StringLiteral] no-varargs-before not sunk 2
# 1| 1: [NullLiteral] null
# 206| 2: [StringLiteral] no-varargs-after not sunk 2
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 207| 3: [ExprStmt] <Expr>;
# 207| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 207| -1: [TypeAccess] TestKt
# 207| 0: [StringLiteral] one-vararg-before not sunk 2
# 207| 1: [StringLiteral] one-vararg sunk 2
# 207| 2: [StringLiteral] one-vararg-after not sunk 2
# 208| 4: [ExprStmt] <Expr>;
# 208| 0: [MethodAccess] varargsTestOnlySinkVarargs(...)
# 208| -1: [TypeAccess] TestKt
# 208| 0: [StringLiteral] two-varargs-before not sunk 2
# 208| 1: [StringLiteral] two-vararg-first sunk 2
# 208| 2: [StringLiteral] two-vararg-second sunk 2
# 208| 3: [StringLiteral] two-varargs-after not sunk 2
# 209| 5: [ExprStmt] <Expr>;
# 209| 0: [MethodAccess] varargsTestOnlySinkVarargs$default(...)
# 209| -1: [TypeAccess] TestKt
# 209| 0: [StringLiteral] no-z-parmeter not sunk 2
# 209| 1: [ArrayCreationExpr] new String[]
# 209| -2: [ArrayInit] {...}
# 209| 0: [StringLiteral] no-z-parameter first vararg sunk 2
# 209| 1: [StringLiteral] no-z-parameter second vararg sunk 2
# 209| -1: [TypeAccess] String
# 209| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 212| 8: [Method] varargsTestOnlySinkRegularArgs
# 212| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 212| 0: [Parameter] x
# 212| 0: [TypeAccess] String
# 212| 1: [Parameter] y
# 212| 0: [TypeAccess] String[]
# 212| 0: [WildcardTypeAccess] ? ...
# 212| 0: [TypeAccess] String
# 212| 2: [Parameter] z
# 212| 0: [TypeAccess] String
# 212| 5: [BlockStmt] { ... }
# 213| 0: [ExprStmt] <Expr>;
# 213| 0: [MethodAccess] sink(...)
# 213| -1: [TypeAccess] TestKt
# 213| 0: [VarAccess] x
# 214| 1: [ExprStmt] <Expr>;
# 214| 0: [MethodAccess] sink(...)
# 214| -1: [TypeAccess] TestKt
# 214| 0: [VarAccess] z
# 212| 9: [Method] varargsTestOnlySinkRegularArgs$default
# 212| 3: [TypeAccess] Unit
#-----| 4: (Parameters)
# 212| 0: [Parameter] p0
# 212| 0: [TypeAccess] String
# 212| 1: [Parameter] p1
# 212| 0: [TypeAccess] String[]
# 212| 2: [Parameter] p2
# 212| 0: [TypeAccess] String
# 212| 3: [Parameter] p3
# 212| 0: [TypeAccess] int
# 212| 4: [Parameter] p4
# 212| 0: [TypeAccess] Object
# 212| 5: [BlockStmt] { ... }
# 212| 0: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 1
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p0
# 212| 1: [StringLiteral] before-vararg-default sunk 3
# 212| 1: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 2
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p1
# 212| 1: [ArrayCreationExpr] new String[]
# 212| -2: [ArrayInit] {...}
# 212| 0: [StringLiteral] first-vararg-default not sunk 3
# 212| 1: [StringLiteral] second-vararg-default not sunk 3
# 212| -1: [TypeAccess] String
# 212| 0: [IntegerLiteral] 2
# 212| 2: [IfStmt] if (...)
# 212| 0: [EQExpr] ... == ...
# 212| 0: [AndBitwiseExpr] ... & ...
# 212| 0: [IntegerLiteral] 4
# 212| 1: [VarAccess] p3
# 212| 1: [IntegerLiteral] 0
# 212| 1: [ExprStmt] <Expr>;
# 212| 0: [AssignExpr] ...=...
# 212| 0: [VarAccess] p2
# 212| 1: [StringLiteral] after-vararg-default sunk 3
# 212| 3: [ReturnStmt] return ...
# 212| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 212| -1: [TypeAccess] TestKt
# 212| 0: [VarAccess] p0
# 212| 1: [VarAccess] p1
# 212| 2: [VarAccess] p2
# 217| 10: [Method] varargsUserOnlySinkRegularArgs
# 217| 3: [TypeAccess] Unit
# 217| 5: [BlockStmt] { ... }
# 218| 0: [ExprStmt] <Expr>;
# 218| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 218| -1: [TypeAccess] TestKt
# 1| 0: [NullLiteral] null
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 0
# 1| 4: [NullLiteral] null
# 219| 1: [ExprStmt] <Expr>;
# 219| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 219| -1: [TypeAccess] TestKt
# 219| 0: [StringLiteral] no-varargs-before, no-z-parameter sunk 3
# 1| 1: [NullLiteral] null
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 1
# 1| 4: [NullLiteral] null
# 220| 2: [ExprStmt] <Expr>;
# 220| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 220| -1: [TypeAccess] TestKt
# 220| 0: [StringLiteral] no-varargs-before sunk 3
# 1| 1: [NullLiteral] null
# 220| 2: [StringLiteral] no-varargs-after sunk 3
# 1| 3: [IntegerLiteral] 5
# 1| 4: [NullLiteral] null
# 221| 3: [ExprStmt] <Expr>;
# 221| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 221| -1: [TypeAccess] TestKt
# 221| 0: [StringLiteral] one-vararg-before sunk 3
# 221| 1: [StringLiteral] one-vararg not sunk 3
# 221| 2: [StringLiteral] one-vararg-after sunk 3
# 222| 4: [ExprStmt] <Expr>;
# 222| 0: [MethodAccess] varargsTestOnlySinkRegularArgs(...)
# 222| -1: [TypeAccess] TestKt
# 222| 0: [StringLiteral] two-varargs-before sunk 3
# 222| 1: [StringLiteral] two-vararg-first not sunk 3
# 222| 2: [StringLiteral] two-vararg-second not sunk 3
# 222| 3: [StringLiteral] two-varargs-after sunk 3
# 223| 5: [ExprStmt] <Expr>;
# 223| 0: [MethodAccess] varargsTestOnlySinkRegularArgs$default(...)
# 223| -1: [TypeAccess] TestKt
# 223| 0: [StringLiteral] no-z-parmeter sunk 3
# 223| 1: [ArrayCreationExpr] new String[]
# 223| -2: [ArrayInit] {...}
# 223| 0: [StringLiteral] no-z-parameter first vararg not sunk 3
# 223| 1: [StringLiteral] no-z-parameter second vararg not sunk 3
# 223| -1: [TypeAccess] String
# 223| 0: [IntegerLiteral] 2
# 1| 2: [NullLiteral] null
# 1| 3: [IntegerLiteral] 3
# 1| 4: [NullLiteral] null
# 3| 2: [Class] TestMember # 3| 2: [Class] TestMember
# 3| 1: [Constructor] TestMember # 3| 1: [Constructor] TestMember
# 3| 5: [BlockStmt] { ... } # 3| 5: [BlockStmt] { ... }

View File

@@ -13,3 +13,6 @@
| test.kt:171:3:171:97 | f | test.kt:171:3:171:97 | f$default | | test.kt:171:3:171:97 | f | test.kt:171:3:171:97 | f$default |
| test.kt:179:3:179:46 | f | test.kt:179:3:179:46 | f$default | | test.kt:179:3:179:46 | f | test.kt:179:3:179:46 | f$default |
| test.kt:180:3:180:34 | f | test.kt:180:3:180:34 | f$default | | test.kt:180:3:180:34 | f | test.kt:180:3:180:34 | f$default |
| test.kt:184:1:188:1 | varargsTest | test.kt:184:1:188:1 | varargsTest$default |
| test.kt:199:1:201:1 | varargsTestOnlySinkVarargs | test.kt:199:1:201:1 | varargsTestOnlySinkVarargs$default |
| test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs | test.kt:212:1:215:1 | varargsTestOnlySinkRegularArgs$default |

View File

@@ -180,3 +180,45 @@ class TestOverloadsWithDefaults {
fun f(z: String, w: Int = 0) { } fun f(z: String, w: Int = 0) { }
} }
fun varargsTest(x: String = "before-vararg-default sunk", vararg y: String = arrayOf("first-vararg-default sunk", "second-vararg-default sunk"), z: String = "after-vararg-default sunk") {
sink(x)
sink(y[0])
sink(z)
}
fun varargsUser() {
varargsTest()
varargsTest(x = "no-varargs-before, no-z-parameter sunk")
varargsTest(x = "no-varargs-before sunk", z = "no-varargs-after sunk")
varargsTest(x = "one-vararg-before sunk", "one-vararg sunk", z = "one-vararg-after sunk")
varargsTest(x = "two-varargs-before sunk", "two-vararg-first sunk", "two-vararg-second sunk", z = "two-varargs-after sunk")
varargsTest("no-z-parmeter sunk", "no-z-parameter first vararg sunk", "no-z-parameter second vararg sunk")
}
fun varargsTestOnlySinkVarargs(x: String = "before-vararg-default not sunk 2", vararg y: String = arrayOf("first-vararg-default sunk 2", "second-vararg-default sunk 2"), z: String = "after-vararg-default not sunk 2") {
sink(y[0])
}
fun varargsUserOnlySinkVarargs() {
varargsTestOnlySinkVarargs()
varargsTestOnlySinkVarargs(x = "no-varargs-before, no-z-parameter not sunk 2")
varargsTestOnlySinkVarargs(x = "no-varargs-before not sunk 2", z = "no-varargs-after not sunk 2")
varargsTestOnlySinkVarargs(x = "one-vararg-before not sunk 2", "one-vararg sunk 2", z = "one-vararg-after not sunk 2")
varargsTestOnlySinkVarargs(x = "two-varargs-before not sunk 2", "two-vararg-first sunk 2", "two-vararg-second sunk 2", z = "two-varargs-after not sunk 2")
varargsTestOnlySinkVarargs("no-z-parmeter not sunk 2", "no-z-parameter first vararg sunk 2", "no-z-parameter second vararg sunk 2")
}
fun varargsTestOnlySinkRegularArgs(x: String = "before-vararg-default sunk 3", vararg y: String = arrayOf("first-vararg-default not sunk 3", "second-vararg-default not sunk 3"), z: String = "after-vararg-default sunk 3") {
sink(x)
sink(z)
}
fun varargsUserOnlySinkRegularArgs() {
varargsTestOnlySinkRegularArgs()
varargsTestOnlySinkRegularArgs(x = "no-varargs-before, no-z-parameter sunk 3")
varargsTestOnlySinkRegularArgs(x = "no-varargs-before sunk 3", z = "no-varargs-after sunk 3")
varargsTestOnlySinkRegularArgs(x = "one-vararg-before sunk 3", "one-vararg not sunk 3", z = "one-vararg-after sunk 3")
varargsTestOnlySinkRegularArgs(x = "two-varargs-before sunk 3", "two-vararg-first not sunk 3", "two-vararg-second not sunk 3", z = "two-varargs-after sunk 3")
varargsTestOnlySinkRegularArgs("no-z-parmeter sunk 3", "no-z-parameter first vararg not sunk 3", "no-z-parameter second vararg not sunk 3")
}

View File

@@ -16,6 +16,9 @@
| test.kt:36:40:36:41 | 30 | test.kt:25:10:25:18 | ...[...] | | test.kt:36:40:36:41 | 30 | test.kt:25:10:25:18 | ...[...] |
| test.kt:36:44:36:45 | 31 | test.kt:25:10:25:18 | ...[...] | | test.kt:36:44:36:45 | 31 | test.kt:25:10:25:18 | ...[...] |
| test.kt:36:48:36:49 | 32 | test.kt:25:10:25:18 | ...[...] | | test.kt:36:48:36:49 | 32 | test.kt:25:10:25:18 | ...[...] |
| test.kt:37:33:37:34 | 41 | test.kt:29:10:29:18 | ...[...] |
| test.kt:37:37:37:38 | 42 | test.kt:29:10:29:18 | ...[...] |
| test.kt:37:41:37:42 | 43 | test.kt:29:10:29:18 | ...[...] |
| test.kt:41:26:41:27 | 51 | test.kt:9:10:9:18 | ...[...] | | test.kt:41:26:41:27 | 51 | test.kt:9:10:9:18 | ...[...] |
| test.kt:41:30:41:31 | 52 | test.kt:9:10:9:18 | ...[...] | | test.kt:41:30:41:31 | 52 | test.kt:9:10:9:18 | ...[...] |
| test.kt:41:34:41:35 | 53 | test.kt:9:10:9:18 | ...[...] | | test.kt:41:34:41:35 | 53 | test.kt:9:10:9:18 | ...[...] |

View File

@@ -0,0 +1,24 @@
fun fn0(f: Function0<Unit>) = f()
fun fn1() {
var c = true
while (c) { // TODO: false positive
fn0 {
c = false
}
}
var d = true
while (d) {
fn0 {
println(d)
}
}
val e = true
while (e) {
fn0 {
println(e)
}
}
}

View File

@@ -0,0 +1,3 @@
| A.kt:5:12:5:12 | c | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:5:5:9:5 | while (...) | Loop |
| A.kt:12:12:12:12 | d | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:12:5:16:5 | while (...) | Loop |
| A.kt:19:12:19:12 | e | $@ might not terminate, as this loop condition is constant within the loop. | A.kt:19:5:23:5 | while (...) | Loop |

View File

@@ -0,0 +1 @@
Likely Bugs/Termination/ConstantLoopCondition.ql

View File

@@ -0,0 +1,11 @@
fun fn() {
try {
val l = listOf(1, 2, 3)
l.forEachIndexed { index, _ -> println(index) }
val p = Pair(1, 2)
val (first, _) = p
} catch (_: Exception) {
// expected
}
}

View File

@@ -0,0 +1 @@
Compatibility/JDK9/UnderscoreIdentifier.ql

View File

@@ -0,0 +1,7 @@
| Test.kt:3:9:3:31 | List<Integer> l |
| Test.kt:4:28:4:32 | index |
| Test.kt:4:35:4:35 | p1 |
| Test.kt:6:9:6:26 | Pair<Integer,Integer> p |
| Test.kt:7:14:7:18 | int first |
| Test.kt:7:26:7:26 | Pair<Integer,Integer> tmp0_container |
| Test.kt:8:14:8:25 | Exception _ |

View File

@@ -0,0 +1,3 @@
import java
query predicate variables(Variable v) { v.fromSource() }

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-all name: codeql/javascript-all
version: 0.3.2 version: 0.3.3-dev
groups: javascript groups: javascript
dbscheme: semmlecode.javascript.dbscheme dbscheme: semmlecode.javascript.dbscheme
extractor: javascript extractor: javascript

View File

@@ -1,5 +1,5 @@
name: codeql/javascript-queries name: codeql/javascript-queries
version: 0.4.2 version: 0.4.3-dev
groups: groups:
- javascript - javascript
- queries - queries

View File

@@ -1,3 +1,3 @@
name: codeql/suite-helpers name: codeql/suite-helpers
version: 0.3.2 version: 0.3.3-dev
groups: shared groups: shared

View File

@@ -25,6 +25,17 @@
"description": "has relative path", "description": "has relative path",
}, },
"debug based on location": {
"scope": "ql",
"prefix": "debug based on location",
"body": [
"$1.getLocation().getFile().getShortName() = \"$2\" and",
"$1.getLocation().getStartLine() = $3 and",
],
"description": "debug based on location",
},
"Exists": { "Exists": {
"scope": "ql", "scope": "ql",
"prefix": "exists", "prefix": "exists",

View File

@@ -1,5 +1,5 @@
name: codeql/python-all name: codeql/python-all
version: 0.6.2 version: 0.6.3-dev
groups: python groups: python
dbscheme: semmlecode.python.dbscheme dbscheme: semmlecode.python.dbscheme
extractor: python extractor: python

View File

@@ -1,5 +1,5 @@
name: codeql/python-queries name: codeql/python-queries
version: 0.5.2 version: 0.5.3-dev
groups: groups:
- python - python
- queries - queries

View File

@@ -0,0 +1,4 @@
---
category: minorAnalysis
---
* There was a bug in `TaintTracking::localTaint` and `TaintTracking::localTaintStep` such that they only tracked non-value-preserving flow steps. They have been fixed and now also include value-preserving steps.

View File

@@ -36,6 +36,13 @@ private module Cached {
not s instanceof ModuleBase and not s instanceof ModuleBase and
result = getEnclosingMethod(s.getOuterScope()) result = getEnclosingMethod(s.getOuterScope())
} }
cached
Toplevel getEnclosingToplevel(Scope s) {
result = s
or
result = getEnclosingToplevel(s.getOuterScope())
}
} }
private import Cached private import Cached
@@ -66,6 +73,9 @@ class AstNode extends TAstNode {
/** Gets the enclosing method, if any. */ /** Gets the enclosing method, if any. */
final MethodBase getEnclosingMethod() { result = getEnclosingMethod(scopeOfInclSynth(this)) } 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. */ /** Gets a textual representation of this node. */
cached cached
string toString() { none() } string toString() { none() }

View File

@@ -271,10 +271,7 @@ module Http {
/** Gets the URL pattern for this route, if it can be statically determined. */ /** Gets the URL pattern for this route, if it can be statically determined. */
string getUrlPattern() { string getUrlPattern() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode | result = this.getUrlPatternArg().getALocalSource().getConstantValue().getStringlikeValue()
this.getUrlPatternArg().getALocalSource() = DataFlow::exprNode(strNode) and
result = strNode.getExpr().getConstantValue().getStringlikeValue()
)
} }
/** /**
@@ -538,10 +535,12 @@ module Http {
/** Gets the mimetype of this HTTP response, if it can be statically determined. */ /** Gets the mimetype of this HTTP response, if it can be statically determined. */
string getMimetype() { string getMimetype() {
exists(CfgNodes::ExprNodes::StringlikeLiteralCfgNode strNode | result =
this.getMimetypeOrContentTypeArg().getALocalSource() = DataFlow::exprNode(strNode) and this.getMimetypeOrContentTypeArg()
result = strNode.getExpr().getConstantValue().getStringlikeValue().splitAt(";", 0) .getALocalSource()
) .getConstantValue()
.getStringlikeValue()
.splitAt(";", 0)
or or
not exists(this.getMimetypeOrContentTypeArg()) and not exists(this.getMimetypeOrContentTypeArg()) and
result = this.getMimetypeDefault() result = this.getMimetypeDefault()

View File

@@ -170,6 +170,24 @@ module ConstantValue {
/** A constant `nil` value. */ /** A constant `nil` value. */
class ConstantNilValue extends ConstantValue, TNil { } 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. */ /** An access to a constant. */
@@ -252,6 +270,20 @@ private class ConstantReadAccessSynth extends ConstantAccess, TConstantReadAcces
final override predicate hasGlobalScope() { value.matches("::%") } final override predicate hasGlobalScope() { value.matches("::%") }
} }
private class ConstantWriteAccessSynth extends ConstantAccess, TConstantWriteAccessSynth {
private string value;
ConstantWriteAccessSynth() { this = TConstantWriteAccessSynth(_, _, value) }
final override string getName() {
if this.hasGlobalScope() then result = value.suffix(2) else result = value
}
final override Expr getScopeExpr() { synthChild(this, 0, result) }
final override predicate hasGlobalScope() { value.matches("::%") }
}
/** /**
* A use (read) of a constant. * A use (read) of a constant.
* *
@@ -323,7 +355,9 @@ class ConstantReadAccess extends ConstantAccess {
*/ */
class ConstantWriteAccess extends ConstantAccess { class ConstantWriteAccess extends ConstantAccess {
ConstantWriteAccess() { ConstantWriteAccess() {
explicitAssignmentNode(toGenerated(this), _) or this instanceof TNamespace explicitAssignmentNode(toGenerated(this), _) or
this instanceof TNamespace or
this instanceof TConstantWriteAccessSynth
} }
override string getAPrimaryQlClass() { result = "ConstantWriteAccess" } override string getAPrimaryQlClass() { result = "ConstantWriteAccess" }

View File

@@ -61,7 +61,7 @@ class ArgumentList extends Expr, TArgumentList {
private class LhsExpr_ = private class LhsExpr_ =
TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or TVariableAccess or TTokenConstantAccess or TScopeResolutionConstantAccess or TMethodCall or
TDestructuredLhsExpr; TDestructuredLhsExpr or TConstantWriteAccessSynth;
/** /**
* A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of * A "left-hand-side" (LHS) expression. An `LhsExpr` can occur on the left-hand side of

View File

@@ -3,6 +3,7 @@ private import codeql.ruby.CFG
private import internal.AST private import internal.AST
private import internal.Module private import internal.Module
private import internal.TreeSitter private import internal.TreeSitter
private import internal.Scope
/** /**
* A representation of a run-time `module` or `class` value. * A representation of a run-time `module` or `class` value.
@@ -23,9 +24,30 @@ class Module extends TModule {
/** Gets an `include`d module. */ /** Gets an `include`d module. */
Module getAnIncludedModule() { result = getAnIncludedModule(this) } 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. */ /** Holds if this module is a class. */
pragma[noinline] pragma[noinline]
predicate isClass() { this.getADeclaration() instanceof ClassDeclaration } predicate isClass() {
this.getADeclaration() instanceof ClassDeclaration
or
// If another class extends this, but we can't see the class declaration, assume it's a class
getSuperClass(_) = this
}
/** Gets a textual representation of this module. */ /** Gets a textual representation of this module. */
string toString() { string toString() {
@@ -58,6 +80,99 @@ class Module extends TModule {
loc.getStartColumn() loc.getStartColumn()
) )
} }
/** Gets a constant or `self` access that refers to this module. */
private Expr getAnImmediateReferenceBase() {
resolveConstantReadAccess(result) = this
or
result.(SelfVariableAccess).getVariable() = this.getADeclaration().getModuleSelfVariable()
}
/** Gets a singleton class that augments this module object. */
SingletonClass getASingletonClass() { result.getValue() = this.getAnImmediateReferenceBase() }
/**
* Gets a singleton method on this module, either declared as a singleton method
* or an instance method on a singleton class.
*
* Does not take inheritance into account.
*/
MethodBase getAnOwnSingletonMethod() {
result.(SingletonMethod).getObject() = this.getAnImmediateReferenceBase()
or
result = this.getASingletonClass().getAMethod().(Method)
}
/**
* Gets an instance method named `name` declared in this module.
*
* Does not take inheritance into account.
*/
Method getOwnInstanceMethod(string name) { result = this.getADeclaration().getMethod(name) }
/**
* Gets an instance method declared in this module.
*
* Does not take inheritance into account.
*/
Method getAnOwnInstanceMethod() { result = this.getADeclaration().getMethod(_) }
/**
* Gets the instance method named `name` available in this module, including methods inherited
* from ancestors.
*/
Method getInstanceMethod(string name) { result = lookupMethod(this, name) }
/**
* Gets an instance method available in this module, including methods inherited
* from ancestors.
*/
Method getAnInstanceMethod() { result = lookupMethod(this, _) }
/** Gets a constant or `self` access that refers to this module. */
Expr getAnImmediateReference() {
result = this.getAnImmediateReferenceBase()
or
result.(SelfVariableAccess).getVariable().getDeclaringScope() = this.getAnOwnSingletonMethod()
}
pragma[nomagic]
private string getEnclosingModuleName() {
exists(string qname |
qname = this.getQualifiedName() and
result = qname.regexpReplaceAll("::[^:]*$", "") and
qname != result
)
}
pragma[nomagic]
private string getOwnModuleName() {
result = this.getQualifiedName().regexpReplaceAll("^.*::", "")
}
/**
* Gets the enclosing module, as it appears in the qualified name of this module.
*
* For example, the parent module of `A::B` is `A`, and `A` itself has no parent module.
*/
pragma[nomagic]
Module getParentModule() { result.getQualifiedName() = this.getEnclosingModuleName() }
/**
* Gets a module named `name` declared inside this one (not aliased), provided
* that such a module is defined or reopened in the current codebase.
*
* For example, for `A::B` the nested module named `C` would be `A::B::C`.
*
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
* module whose qualified name is not `A::B::C`, then it will not be found by
* this predicate.
*/
pragma[nomagic]
Module getNestedModule(string name) {
result.getParentModule() = this and
result.getOwnModuleName() = name
}
} }
/** /**
@@ -136,6 +251,46 @@ class ModuleBase extends BodyStmt, Scope, TModuleBase {
/** Gets the representation of the run-time value of this module or class. */ /** Gets the representation of the run-time value of this module or class. */
Module getModule() { none() } 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

@@ -116,6 +116,9 @@ private module Cached {
TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) { TConstantReadAccessSynth(Ast::AstNode parent, int i, string value) {
mkSynthChild(ConstantReadAccessKind(value), parent, i) mkSynthChild(ConstantReadAccessKind(value), parent, i)
} or } or
TConstantWriteAccessSynth(Ast::AstNode parent, int i, string value) {
mkSynthChild(ConstantWriteAccessKind(value), parent, i)
} or
TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or TDefinedExpr(Ruby::Unary g) { g instanceof @ruby_unary_definedquestion } or
TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or TDelimitedSymbolLiteral(Ruby::DelimitedSymbol g) or
TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) { TDestructuredLeftAssignment(Ruby::DestructuredLeftAssignment g) {
@@ -373,12 +376,13 @@ private module Cached {
class TAstNodeSynth = class TAstNodeSynth =
TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or TAddExprSynth or TAssignExprSynth or TBitwiseAndExprSynth or TBitwiseOrExprSynth or
TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or TBitwiseXorExprSynth or TBraceBlockSynth or TClassVariableAccessSynth or
TConstantReadAccessSynth or TDivExprSynth or TExponentExprSynth or TConstantReadAccessSynth or TConstantWriteAccessSynth or TDivExprSynth or
TGlobalVariableAccessSynth or TIfSynth or TInstanceVariableAccessSynth or TExponentExprSynth or TGlobalVariableAccessSynth or TIfSynth or
TIntegerLiteralSynth or TLShiftExprSynth or TLocalVariableAccessSynth or TInstanceVariableAccessSynth or TIntegerLiteralSynth or TLShiftExprSynth or
TLogicalAndExprSynth or TLogicalOrExprSynth or TMethodCallSynth or TModuloExprSynth or TLocalVariableAccessSynth or TLogicalAndExprSynth or TLogicalOrExprSynth or
TMulExprSynth or TNilLiteralSynth or TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TMethodCallSynth or TModuloExprSynth or TMulExprSynth or TNilLiteralSynth or
TSimpleParameterSynth or TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth; TRShiftExprSynth or TRangeLiteralSynth or TSelfSynth or TSimpleParameterSynth or
TSplatExprSynth or TStmtSequenceSynth or TSubExprSynth;
/** /**
* Gets the underlying TreeSitter entity for a given AST node. This does not * Gets the underlying TreeSitter entity for a given AST node. This does not
@@ -565,6 +569,8 @@ private module Cached {
or or
result = TConstantReadAccessSynth(parent, i, _) result = TConstantReadAccessSynth(parent, i, _)
or or
result = TConstantWriteAccessSynth(parent, i, _)
or
result = TDivExprSynth(parent, i) result = TDivExprSynth(parent, i)
or or
result = TExponentExprSynth(parent, i) result = TExponentExprSynth(parent, i)
@@ -672,7 +678,8 @@ class TMethodCall =
class TSuperCall = TTokenSuperCall or TRegularSuperCall; class TSuperCall = TTokenSuperCall or TRegularSuperCall;
class TConstantAccess = class TConstantAccess =
TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or TConstantReadAccessSynth; TTokenConstantAccess or TScopeResolutionConstantAccess or TNamespace or
TConstantReadAccessSynth or TConstantWriteAccessSynth;
class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop; class TControlExpr = TConditionalExpr or TCaseExpr or TCaseMatch or TLoop;

View File

@@ -1,11 +1,13 @@
private import codeql.ruby.AST private import codeql.ruby.AST
private import Scope as Scope
// Names of built-in modules and classes // Names of built-in modules and classes
private string builtin() { private string builtin() {
result = result =
[ [
"Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass", "Object", "Kernel", "BasicObject", "Class", "Module", "NilClass", "FalseClass", "TrueClass",
"Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "Symbol", "Proc" "Numeric", "Integer", "Float", "Rational", "Complex", "Array", "Hash", "String", "Symbol",
"Proc",
] ]
} }
@@ -16,6 +18,8 @@ private module Cached {
TResolved(string qName) { TResolved(string qName) {
qName = builtin() qName = builtin()
or or
qName = getAnAssumedGlobalConst()
or
qName = namespaceDeclaration(_) qName = namespaceDeclaration(_)
} or } or
TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) } TUnresolved(Namespace n) { not exists(namespaceDeclaration(n)) }
@@ -38,7 +42,10 @@ private module Cached {
Module getSuperClass(Module cls) { Module getSuperClass(Module cls) {
cls = TResolved("Object") and result = TResolved("BasicObject") cls = TResolved("Object") and result = TResolved("BasicObject")
or or
cls = TResolved(["Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass"]) and cls =
TResolved([
"Module", "Numeric", "Array", "Hash", "FalseClass", "TrueClass", "NilClass", "String"
]) and
result = TResolved("Object") result = TResolved("Object")
or or
cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and cls = TResolved(["Integer", "Float", "Rational", "Complex"]) and
@@ -58,6 +65,12 @@ private module Cached {
forex(ClassDeclaration d | d = cls.getADeclaration() | forex(ClassDeclaration d | d = cls.getADeclaration() |
not exists(resolveConstantReadAccess(d.getSuperclassExpr())) not exists(resolveConstantReadAccess(d.getSuperclassExpr()))
) )
or
// If a module is used as a base class of another class, but we don't see its class declaration
// treat it as a class extending Object, so its subclasses transitively extend Object.
result = TResolved("Object") and
not cls.getADeclaration() instanceof ClassDeclaration and
cls = resolveConstantReadAccess(any(ClassDeclaration d).getSuperclassExpr())
) )
} }
@@ -65,7 +78,7 @@ private module Cached {
( (
m = resolveConstantReadAccess(c.getReceiver()) m = resolveConstantReadAccess(c.getReceiver())
or or
m = enclosingModule(c).getModule() and m = enclosingModuleNoBlock(c).getModule() and
c.getReceiver() instanceof SelfVariableAccess c.getReceiver() instanceof SelfVariableAccess
) and ) and
result = resolveConstantReadAccess(c.getAnArgument()) result = resolveConstantReadAccess(c.getAnArgument())
@@ -388,11 +401,23 @@ private module ResolveImpl {
result = resolveConstantWriteAccessRec(c, _, _) result = resolveConstantWriteAccessRec(c, _, _)
} }
/**
* Gets the name of a constant `C` that we assume to be defined in the top-level because
* it is referenced in a way that can only resolve to a top-level constant.
*/
string getAnAssumedGlobalConst() {
exists(ConstantAccess access |
not exists(access.getScopeExpr()) and
result = access.getName() and
isToplevel(access)
)
}
pragma[nomagic] pragma[nomagic]
private string isDefinedConstantNonRec(string container, string name) { private string isDefinedConstantNonRec(string container, string name) {
result = resolveConstantWriteAccessNonRec(_, container, name) result = resolveConstantWriteAccessNonRec(_, container, name)
or or
result = builtin() and result = [builtin(), getAnAssumedGlobalConst()] and
name = result and name = result and
container = "Object" container = "Object"
} }
@@ -447,7 +472,7 @@ private module ResolveImpl {
result = resolveConstantReadAccess(this.getReceiver(), _) result = resolveConstantReadAccess(this.getReceiver(), _)
or or
exists(ModuleBase encl | exists(ModuleBase encl |
encl = enclosingModule(this) and encl = enclosingModuleNoBlock(this) and
result = [qualifiedModuleNameNonRec(encl, _, _), qualifiedModuleNameRec(encl, _, _)] result = [qualifiedModuleNameNonRec(encl, _, _), qualifiedModuleNameRec(encl, _, _)]
| |
this.getReceiver() instanceof SelfVariableAccess this.getReceiver() instanceof SelfVariableAccess
@@ -495,7 +520,20 @@ private module ResolveImpl {
private import ResolveImpl private import ResolveImpl
/** /**
* A variant of AstNode::getEnclosingModule that excludes * Gets an enclosing scope of `scope`, stopping at the first module or block.
*
* Includes `scope` itself and the final module/block.
*/
private Scope enclosingScopesNoBlock(Scope scope) {
result = scope
or
not scope instanceof ModuleBase and
not scope instanceof Block and
result = enclosingScopesNoBlock(scope.getOuterScope())
}
/**
* A variant of `AstNode::getEnclosingModule` that excludes
* results that are enclosed in a block. This is a bit wrong because * results that are enclosed in a block. This is a bit wrong because
* it could lead to false negatives. However, `include` statements in * it could lead to false negatives. However, `include` statements in
* blocks are very rare in normal code. The majority of cases are in calls * blocks are very rare in normal code. The majority of cases are in calls
@@ -503,15 +541,10 @@ private import ResolveImpl
* methods evaluate the block in the context of some other module/class instead of * methods evaluate the block in the context of some other module/class instead of
* the enclosing one. * the enclosing one.
*/ */
private ModuleBase enclosingModule(AstNode node) { private ModuleBase enclosingModuleNoBlock(Stmt node) {
result = node.getParent() // Note: don't rely on AstNode.getParent() here.
or // Instead use Scope.getOuterScope() to correctly handle the scoping of things like Namespace.getScopeExpr().
exists(AstNode mid | result = enclosingScopesNoBlock(Scope::scopeOfInclSynth(node))
result = enclosingModule(mid) and
mid = node.getParent() and
not mid instanceof ModuleBase and
not mid instanceof Block
)
} }
private Module getAncestors(Module m) { private Module getAncestors(Module m) {

View File

@@ -42,7 +42,8 @@ newtype SynthKind =
StmtSequenceKind() or StmtSequenceKind() or
SelfKind(SelfVariable v) or SelfKind(SelfVariable v) or
SubExprKind() or SubExprKind() or
ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } ConstantReadAccessKind(string value) { any(Synthesis s).constantReadAccess(value) } or
ConstantWriteAccessKind(string value) { any(Synthesis s).constantWriteAccess(value) }
/** /**
* An AST child. * An AST child.
@@ -107,6 +108,11 @@ class Synthesis extends TSynthesis {
*/ */
predicate constantReadAccess(string name) { none() } predicate constantReadAccess(string name) { none() }
/**
* Holds if a constant write access of `name` is needed.
*/
predicate constantWriteAccess(string name) { none() }
/** /**
* Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction. * Holds if `n` should be excluded from `ControlFlowTree` in the CFG construction.
*/ */
@@ -493,6 +499,231 @@ private module AssignOperationDesugar {
} }
} }
/**
* An assignment operation where the left-hand side is a constant
* without scope expression, such as`FOO` or `::Foo`.
*/
private class ConstantAssignOperation extends AssignOperation {
string name;
pragma[nomagic]
ConstantAssignOperation() {
name =
any(Ruby::Constant constant | TTokenConstantAccess(constant) = this.getLeftOperand())
.getValue()
or
name =
"::" +
any(Ruby::Constant constant |
TScopeResolutionConstantAccess(any(Ruby::ScopeResolution g | not exists(g.getScope())),
constant) = this.getLeftOperand()
).getValue()
}
final string getName() { result = name }
}
pragma[nomagic]
private predicate constantAssignOperationSynthesis(AstNode parent, int i, Child child) {
exists(ConstantAssignOperation cao |
parent = cao and
i = -1 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(cao, -1) |
parent = assign and
i = 0 and
child = childRef(cao.getLeftOperand())
or
parent = assign and
i = 1 and
child = SynthChild(getKind(cao))
or
parent = getSynthChild(assign, 1) and
(
i = 0 and
child = SynthChild(ConstantReadAccessKind(cao.getName()))
or
i = 1 and
child = childRef(cao.getRightOperand())
)
)
)
}
/**
* ```rb
* FOO += y
* ```
*
* desugars to
*
* ```rb
* FOO = FOO + y
* ```
*/
private class ConstantAssignOperationSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
constantAssignOperationSynthesis(parent, i, child)
}
final override predicate constantReadAccess(string name) {
name = any(ConstantAssignOperation o).getName()
}
final override predicate location(AstNode n, Location l) {
exists(ConstantAssignOperation cao, BinaryOperation bo |
bo = cao.getDesugared().(AssignExpr).getRightOperand()
|
n = bo and
l = getAssignOperationLocation(cao)
or
n = bo.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
)
}
}
/**
* An assignment operation where the left-hand side is a constant
* with scope expression, such as `expr::FOO`.
*/
private class ScopeResolutionAssignOperation extends AssignOperation {
string name;
Expr scope;
pragma[nomagic]
ScopeResolutionAssignOperation() {
exists(Ruby::Constant constant, Ruby::ScopeResolution g |
TScopeResolutionConstantAccess(g, constant) = this.getLeftOperand() and
name = constant.getValue() and
toGenerated(scope) = g.getScope()
)
}
final string getName() { result = name }
final Expr getScopeExpr() { result = scope }
}
pragma[nomagic]
private predicate scopeResolutionAssignOperationSynthesis(AstNode parent, int i, Child child) {
exists(ScopeResolutionAssignOperation cao |
parent = cao and
i = -1 and
child = SynthChild(StmtSequenceKind())
or
exists(AstNode stmts | stmts = TStmtSequenceSynth(cao, -1) |
parent = stmts and
i = 0 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(stmts, 0) |
parent = assign and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
or
parent = assign and
i = 1 and
child = childRef(cao.getScopeExpr())
)
or
parent = stmts and
i = 1 and
child = SynthChild(AssignExprKind())
or
exists(AstNode assign | assign = TAssignExprSynth(stmts, 1) |
parent = assign and
i = 0 and
child = SynthChild(ConstantWriteAccessKind(cao.getName()))
or
exists(AstNode cwa | cwa = TConstantWriteAccessSynth(assign, 0, cao.getName()) |
parent = cwa and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
)
or
parent = assign and
i = 1 and
child = SynthChild(getKind(cao))
or
exists(AstNode op | op = getSynthChild(assign, 1) |
parent = op and
i = 0 and
child = SynthChild(ConstantReadAccessKind(cao.getName()))
or
exists(AstNode cra | cra = TConstantReadAccessSynth(op, 0, cao.getName()) |
parent = cra and
i = 0 and
child = SynthChild(LocalVariableAccessSynthKind(TLocalVariableSynth(cao, 0)))
)
or
parent = op and
i = 1 and
child = childRef(cao.getRightOperand())
)
)
)
)
}
/**
* ```rb
* expr::FOO += y
* ```
*
* desugars to
*
* ```rb
* __synth__0 = expr
* __synth__0::FOO = _synth__0::FOO + y
* ```
*/
private class ScopeResolutionAssignOperationSynthesis extends Synthesis {
final override predicate child(AstNode parent, int i, Child child) {
scopeResolutionAssignOperationSynthesis(parent, i, child)
}
final override predicate constantReadAccess(string name) {
name = any(ScopeResolutionAssignOperation o).getName()
}
final override predicate localVariable(AstNode n, int i) {
n instanceof ScopeResolutionAssignOperation and
i = 0
}
final override predicate constantWriteAccess(string name) { this.constantReadAccess(name) }
final override predicate location(AstNode n, Location l) {
exists(ScopeResolutionAssignOperation cao, StmtSequence stmts | stmts = cao.getDesugared() |
n = stmts.getStmt(0) and
hasLocation(cao.getScopeExpr(), l)
or
exists(AssignExpr assign | assign = stmts.getStmt(1) |
n = assign and hasLocation(cao, l)
or
n = assign.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
or
n = assign.getLeftOperand().(ConstantAccess).getScopeExpr() and
hasLocation(cao.getScopeExpr(), l)
or
exists(BinaryOperation bo | bo = assign.getRightOperand() |
n = bo and
l = getAssignOperationLocation(cao)
or
n = bo.getLeftOperand() and
hasLocation(cao.getLeftOperand(), l)
or
n = bo.getLeftOperand().(ConstantAccess).getScopeExpr() and
hasLocation(cao.getScopeExpr(), l)
)
)
)
}
}
/** An assignment operation where the left-hand side is a method call. */ /** An assignment operation where the left-hand side is a method call. */
private class SetterAssignOperation extends AssignOperation { private class SetterAssignOperation extends AssignOperation {
private MethodCall mc; private MethodCall mc;

View File

@@ -7,7 +7,14 @@ private import internal.ControlFlowGraphImpl
private import internal.Splitting private import internal.Splitting
private import internal.Completion private import internal.Completion
/** An AST node with an associated control-flow graph. */ /**
* An AST node with an associated control-flow graph.
*
* Top-levels, methods, blocks, and lambdas are all CFG scopes.
*
* Note that module declarations are not themselves CFG scopes, as they are part of
* the CFG of the enclosing top-level or callable.
*/
class CfgScope extends Scope instanceof CfgScopeImpl { class CfgScope extends Scope instanceof CfgScopeImpl {
/** Gets the CFG scope that this scope is nested under, if any. */ /** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() { final CfgScope getOuterCfgScope() {

View File

@@ -890,7 +890,12 @@ module Trees {
private class ConstantAccessTree extends PostOrderTree, ConstantAccess { private class ConstantAccessTree extends PostOrderTree, ConstantAccess {
ConstantAccessTree() { ConstantAccessTree() {
not this instanceof ClassDeclaration and not this instanceof ClassDeclaration and
not this instanceof ModuleDeclaration not this instanceof ModuleDeclaration and
// constant accesses with scope expression in compound assignments are desugared
not (
this = any(AssignOperation op).getLeftOperand() and
exists(this.getScopeExpr())
)
} }
final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() } final override predicate propagatesAbnormal(AstNode child) { child = this.getScopeExpr() }

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() } override Location getLocation() { result = this.getBasicBlock().getLocation() }
} }

View File

@@ -375,17 +375,10 @@ private module Cached {
private predicate selfInSingletonMethodFlowsToMethodCallReceiver( private predicate selfInSingletonMethodFlowsToMethodCallReceiver(
RelevantCall call, Module m, string method RelevantCall call, Module m, string method
) { ) {
exists(SsaSelfDefinitionNode self, Module target, MethodBase caller | exists(SsaSelfDefinitionNode self, MethodBase caller |
flowsToMethodCallReceiver(call, self, method) and flowsToMethodCallReceiver(call, self, method) and
target = m.getSuperClass*() and selfInMethod(self.getVariable(), caller, m) and
selfInMethod(self.getVariable(), caller, target) and singletonMethod(caller, _, _)
singletonMethod(caller, _, _) and
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
// methods on Object, if the block is actually evaluated in the context of another class.
// The 'self' inside such a singleton method could then be any class, leading to self-calls
// being resolved to arbitrary singleton methods.
// To remedy this, we do not allow following super-classes all the way to Object.
not (m != target and target = TResolved("Object"))
) )
} }
@@ -441,12 +434,13 @@ private module Cached {
// M.extend(M) // M.extend(M)
// M.instance # <- call // M.instance # <- call
// ``` // ```
exists(Module m | result = lookupSingletonMethod(m, method) | exists(Module m, boolean exact | result = lookupSingletonMethod(m, method, exact) |
// ```rb // ```rb
// def C.singleton; end # <- result // def C.singleton; end # <- result
// C.singleton # <- call // C.singleton # <- call
// ``` // ```
moduleFlowsToMethodCallReceiver(call, m, method) moduleFlowsToMethodCallReceiver(call, m, method) and
exact = true
or or
// ```rb // ```rb
// class C // class C
@@ -454,7 +448,8 @@ private module Cached {
// self.singleton # <- call // self.singleton # <- call
// end // end
// ``` // ```
selfInModuleFlowsToMethodCallReceiver(call, m, method) selfInModuleFlowsToMethodCallReceiver(call, m, method) and
exact = true
or or
// ```rb // ```rb
// class C // class C
@@ -464,7 +459,8 @@ private module Cached {
// end // end
// end // end
// ``` // ```
selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method) selfInSingletonMethodFlowsToMethodCallReceiver(call, m, method) and
exact = false
) )
) )
or or
@@ -796,26 +792,54 @@ private predicate singletonMethodOnModule(MethodBase method, string name, Module
) )
} }
pragma[nomagic]
private MethodBase lookupSingletonMethodDirect(Module m, string name) {
singletonMethodOnModule(result, name, m)
or
exists(DataFlow::LocalSourceNode sourceNode |
sourceNode = trackModuleAccess(m) and
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
flowsToSingletonMethodObject(sourceNode, result, name)
)
}
/** /**
* Holds if `method` is a singleton method named `name`, defined on module * Holds if `method` is a singleton method named `name`, defined on module
* `m`, or any transitive base class of `m`. * `m`, or any transitive base class of `m`.
*/ */
pragma[nomagic] pragma[nomagic]
private MethodBase lookupSingletonMethod(Module m, string name) { private MethodBase lookupSingletonMethod(Module m, string name) {
singletonMethodOnModule(result, name, m) result = lookupSingletonMethodDirect(m, name)
or
// cannot be part of `singletonMethodOnModule` because it would introduce
// negative recursion below
exists(DataFlow::LocalSourceNode sourceNode |
sourceNode = trackModuleAccess(m) and
not m = resolveConstantReadAccess(sourceNode.asExpr().getExpr()) and
flowsToSingletonMethodObject(sourceNode, result, name)
)
or or
// cannot use `lookupSingletonMethodDirect` because it would introduce
// negative recursion
not singletonMethodOnModule(_, name, m) and not singletonMethodOnModule(_, name, m) and
result = lookupSingletonMethod(m.getSuperClass(), name) result = lookupSingletonMethod(m.getSuperClass(), name)
} }
pragma[nomagic]
private MethodBase lookupSingletonMethodInSubClasses(Module m, string name) {
// Singleton methods declared in a block in the top-level may spuriously end up being seen as singleton
// methods on Object, if the block is actually evaluated in the context of another class.
// The 'self' inside such a singleton method could then be any class, leading to self-calls
// being resolved to arbitrary singleton methods.
// To remedy this, we do not allow following super-classes all the way to Object.
not m = TResolved("Object") and
exists(Module sub | sub.getSuperClass() = m |
result = lookupSingletonMethodDirect(sub, name) or
result = lookupSingletonMethodInSubClasses(sub, name)
)
}
pragma[nomagic]
private MethodBase lookupSingletonMethod(Module m, string name, boolean exact) {
result = lookupSingletonMethod(m, name) and
exact in [false, true]
or
result = lookupSingletonMethodInSubClasses(m, name) and
exact = false
}
/** /**
* Holds if `method` is a singleton method named `name`, defined on expression * Holds if `method` is a singleton method named `name`, defined on expression
* `object`, where `object` is not likely to resolve to a module: * `object`, where `object` is not likely to resolve to a module:

View File

@@ -109,11 +109,27 @@ module LocalFlow {
* Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node. * Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node.
*/ */
predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) { predicate localFlowSsaParamInput(Node nodeFrom, Node nodeTo) {
nodeTo = getParameterDefNode(nodeFrom.(ParameterNode).getParameter()) nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter())
or or
nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNode).getMethod()) 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` * Holds if there is a local use-use flow step from `nodeFrom` to `nodeTo`
* involving SSA definition `def`. * involving SSA definition `def`.
@@ -246,6 +262,9 @@ private module Cached {
) )
} or } or
TSsaDefinitionNode(Ssa::Definition def) or TSsaDefinitionNode(Ssa::Definition def) or
TRawNamespaceSelf(Namespace ns) {
not exists(Ssa::SelfDefinition def | def.getSourceVariable() = ns.getModuleSelfVariable())
} or
TNormalParameterNode(Parameter p) { TNormalParameterNode(Parameter p) {
p instanceof SimpleParameter or p instanceof SimpleParameter or
p instanceof OptionalParameter or p instanceof OptionalParameter or
@@ -299,7 +318,7 @@ private module Cached {
predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom) defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo) LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or or
@@ -316,7 +335,7 @@ private module Cached {
predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
or or
defaultValueFlow(nodeTo.(ParameterNode).getParameter(), nodeFrom) defaultValueFlow(nodeTo.(ParameterNodeImpl).getParameter(), nodeFrom)
or or
LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo) LocalFlow::localFlowSsaParamInput(nodeFrom, nodeTo)
or or
@@ -327,7 +346,12 @@ private module Cached {
FlowSummaryImpl::Private::Steps::summaryThroughStepValue(nodeFrom, nodeTo, _) 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 cached
predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) { predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) {
LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) LocalFlow::localFlowStepCommon(nodeFrom, nodeTo)
@@ -366,7 +390,7 @@ private module Cached {
cached cached
predicate isLocalSourceNode(Node n) { predicate isLocalSourceNode(Node n) {
n instanceof ParameterNode n instanceof TParameterNode
or or
// Expressions that can't be reached from another entry definition or expression // Expressions that can't be reached from another entry definition or expression
n instanceof ExprNode and n instanceof ExprNode and
@@ -381,6 +405,8 @@ private module Cached {
or or
// Needed for stores in type tracking // Needed for stores in type tracking
TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _) TypeTrackerSpecific::storeStepIntoSourceNode(_, n, _)
or
n instanceof TRawNamespaceSelf
} }
cached cached
@@ -502,6 +528,38 @@ class SsaSelfDefinitionNode extends LocalSourceNode, SsaDefinitionNode {
Scope getSelfScope() { result = self.getDeclaringScope() } Scope getSelfScope() { result = self.getDeclaringScope() }
} }
/**
* A representative for the created or re-opened class/module in a `Namespace` that does
* not have an SSA definition for `self`.
*/
class RawNamespaceSelf extends NodeImpl, TRawNamespaceSelf {
private Namespace namespace;
RawNamespaceSelf() { this = TRawNamespaceSelf(namespace) }
/** Gets the namespace for which this represents the created or re-opened class/module. */
Namespace getNamespace() { result = namespace }
override CfgScope getCfgScope() { result = namespace.getCfgScope() }
override Location getLocationImpl() { result = namespace.getLocation() }
override string toStringImpl() { result = namespace.toString() }
}
/**
* Gets a representative for the created or re-opened class/module in a `Namespace`.
*
* This is usually the SSA definition for `self`, but if this node does not exist due to lack of liveness,
* it is the `RawNamespaceSelf` node.
*/
LocalSourceNode getNamespaceSelf(Namespace namespace) {
result.(SsaDefinitionNode).getDefinition().(Ssa::SelfDefinition).getSourceVariable() =
namespace.getModuleSelfVariable()
or
result = TRawNamespaceSelf(namespace)
}
/** /**
* A value returning statement, viewed as a node in a data flow graph. * A value returning statement, viewed as a node in a data flow graph.
* *
@@ -1242,7 +1300,7 @@ predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c)
creation.asExpr() = creation.asExpr() =
any(CfgNodes::ExprNodes::MethodCallCfgNode mc | any(CfgNodes::ExprNodes::MethodCallCfgNode mc |
c.asCallable() = mc.getBlock().getExpr() and 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. * Gets a data flow node to which data may flow from this node in one local step.
*/ */
Node getASuccessor() { localFlowStep(this, result) } 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. */ /** 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. * 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 * The value of a parameter at function entry, viewed as a node in a data
* flow graph. * 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. */ /** Gets the parameter corresponding to this node, if any. */
final Parameter getParameter() { result = super.getParameter() } final Parameter getParameter() { result = super.getParameter() }
} }
@@ -174,6 +223,80 @@ class LocalSourceNode extends Node {
*/ */
pragma[inline] pragma[inline]
LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) } LocalSourceNode backtrack(TypeBackTracker t2, TypeBackTracker t) { t2 = t.step(result, this) }
/**
* Gets a node to which data may flow from this node in zero or
* more local data-flow steps.
*/
pragma[inline]
Node getALocalUse() { hasLocalSource(result, this) }
/** Gets a method call where this node flows to the receiver. */
CallNode getAMethodCall() { Cached::hasMethodCall(this, result, _) }
/** Gets a call to a method named `name`, where this node flows to the receiver. */
CallNode getAMethodCall(string name) { Cached::hasMethodCall(this, result, name) }
/** Gets a call `obj.name` with no arguments, where this node flows to `obj`. */
CallNode getAnAttributeRead(string name) {
result = this.getAMethodCall(name) and
result.getNumberOfArguments() = 0
}
/**
* Gets a value assigned to `name` on this object, such as the `x` in `obj.name = x`.
*
* Concretely, this gets the argument of any call to `name=` where this node flows to the receiver.
*/
Node getAnAttributeWriteValue(string name) {
result = this.getAMethodCall(name + "=").getArgument(0)
}
/**
* Gets an access to an element on this node, such as `obj[key]`.
*
* Concretely this gets a call to `[]` with 1 argument, where this node flows to the receiver.
*/
CallNode getAnElementRead() {
result = this.getAMethodCall("[]") and result.getNumberOfArguments() = 1
}
/**
* Gets an access to the element `key` on this node, such as `obj[:key]`.
*
* Concretely this gets a call to `[]` where this node flows to the receiver
* and the first and only argument has the constant value `key`.
*/
CallNode getAnElementRead(ConstantValue key) {
result = this.getAnElementRead() and
key = result.getArgument(0).getConstantValue()
}
private CallNode getAnElementWriteCall() {
result = this.getAMethodCall("[]=") and
result.getNumberOfArguments() = 2
}
/**
* Gets a value stored as an element on this node, such as the `x` in `obj[key] = x`.
*
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver.
*/
Node getAnElementWriteValue() { result = this.getAnElementWriteCall().getArgument(1) }
/**
* Gets the `x` in `obj[key] = x`, where this node flows to `obj`.
*
* Concretely, this gets the second argument from any call to `[]=` where this node flows to the receiver
* and the first argument has constant value `key`.
*/
Node getAnElementWriteValue(ConstantValue key) {
exists(CallNode call |
call = this.getAnElementWriteCall() and
call.getArgument(0).getConstantValue() = key and
result = call.getArgument(1)
)
}
} }
/** /**
@@ -193,18 +316,74 @@ class PostUpdateNode extends Node instanceof PostUpdateNodeImpl {
} }
cached cached
private predicate hasLocalSource(Node sink, Node source) { private module Cached {
// Declaring `source` to be a `SourceNode` currently causes a redundant check in the cached
// recursive case, so instead we check it explicitly here. predicate hasLocalSource(Node sink, Node source) {
source = sink and // Declaring `source` to be a `SourceNode` currently causes a redundant check in the
source instanceof LocalSourceNode // recursive case, so instead we check it explicitly here.
or source = sink and
exists(Node mid | source instanceof LocalSourceNode
hasLocalSource(mid, source) and or
localFlowStepTypeTracker(mid, sink) exists(Node mid | hasLocalSource(mid, source) |
) localFlowStepTypeTracker(mid, sink)
or
// Explicitly include the SSA param input step as type-tracking omits this step.
LocalFlow::localFlowSsaParamInput(mid, sink)
or
LocalFlow::localFlowSsaParamCaptureInput(mid, sink)
)
}
cached
predicate hasMethodCall(LocalSourceNode source, CallNode call, string name) {
source.flowsTo(call.getReceiver()) and
call.getMethodName() = name
}
cached
predicate hasYieldCall(BlockParameterNode block, CallNode yield) {
exists(MethodBase method, YieldCall call |
block.getMethod() = method and
call.getEnclosingMethod() = method and
yield.asExpr().getExpr() = call
)
}
/**
* A place in which a named constant can be looked up during constant lookup.
*/
cached
newtype TConstLookupScope =
/** Look up in a qualified constant name `base::`. */
MkQualifiedLookup(ConstantAccess base) or
/** Look up in the ancestors of `mod`. */
MkAncestorLookup(Module mod) or
/** Look up in a module syntactically nested in a declaration of `mod`. */
MkNestedLookup(Module mod) or
/** Pseudo-scope for accesses that are known to resolve to `mod`. */
MkExactLookup(Module mod)
/**
* Gets a `LocalSourceNode` to represent the constant read or written by `access`.
*/
cached
LocalSourceNode getConstantAccessNode(ConstantAccess access) {
// Namespaces don't evaluate to the constant being accessed, they return the value of their last statement.
// Use the definition of 'self' in the namespace as the representative in this case.
if access instanceof Namespace
then result = getNamespaceSelf(access)
else result.asExpr().getExpr() = access
}
cached
predicate forceCachingInSameStage() { any() }
cached
predicate forceCachingBackref() { exists(any(ConstRef const).getConst(_)) }
} }
private import Cached
/** Gets a node corresponding to expression `e`. */ /** Gets a node corresponding to expression `e`. */
ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e } ExprNode exprNode(CfgNodes::ExprCfgNode e) { result.getExprNode() = e }
@@ -559,3 +738,591 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode {
result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead() result.asExpr() = this.getAMaybeGuardedCapturedDef().getARead()
} }
} }
/**
* A representation of a run-time module or class.
*
* This is equivalent to the type `Ast::Module` but provides data-flow specific methods.
*/
class ModuleNode instanceof Module {
/** Gets a declaration of this module, if any. */
final ModuleBase getADeclaration() { result = super.getADeclaration() }
/** Gets the super class of this module, if any. */
final ModuleNode getSuperClass() { result = super.getSuperClass() }
/** Gets an immediate sub class of this module, if any. */
final ModuleNode getASubClass() { result = super.getASubClass() }
/** Gets a `prepend`ed module. */
final ModuleNode getAPrependedModule() { result = super.getAPrependedModule() }
/** Gets an `include`d module. */
final ModuleNode getAnIncludedModule() { result = super.getAnIncludedModule() }
/** Gets the super class or an included or prepended module. */
final ModuleNode getAnImmediateAncestor() { result = super.getAnImmediateAncestor() }
/** Gets a direct subclass or module including or prepending this one. */
final ModuleNode getAnImmediateDescendent() { result = super.getAnImmediateDescendent() }
/** Gets a module that is transitively subclassed, included, or prepended by this module. */
pragma[inline]
final ModuleNode getAnAncestor() { result = super.getAnAncestor() }
/** Gets a module that transitively subclasses, includes, or prepends this module. */
pragma[inline]
final ModuleNode getADescendent() { result = super.getADescendent() }
/**
* Gets the expression node denoting the super class or an included or prepended module.
*
* For example, `C` is an ancestor expression of `M` in each of the following examples:
* ```rb
* class M < C
* end
*
* module M
* include C
* prepend C
* end
* ```
*/
final ExprNode getAnAncestorExpr() {
result.asExpr().getExpr() = super.getADeclaration().getAnAncestorExpr()
}
/** Holds if this module is a class. */
predicate isClass() { super.isClass() }
/** Gets a textual representation of this module. */
final string toString() { result = super.toString() }
/**
* Gets the qualified name of this module, if any.
*
* Only modules that can be resolved will have a qualified name.
*/
final string getQualifiedName() { result = super.getQualifiedName() }
/** Gets the location of this module. */
final Location getLocation() { result = super.getLocation() }
/**
* Gets `self` in a declaration of this module.
*
* This only gets `self` at the module level, not inside any (singleton) method.
*/
LocalSourceNode getModuleLevelSelf() {
result.(SsaDefinitionNode).getVariable() = super.getADeclaration().getModuleSelfVariable()
}
/**
* Gets `self` in the module declaration or in one of its singleton methods.
*
* Does not take inheritance into account.
*/
LocalSourceNode getAnOwnModuleSelf() {
result = this.getModuleLevelSelf()
or
result = this.getAnOwnSingletonMethod().getSelfParameter()
}
/**
* Gets a call to method `name` on `self` in the module-level scope of this module.
*
* For example,
* ```rb
* module M
* include A # getAModuleLevelCall("include")
* foo :bar # getAModuleLevelCall("foo")
* end
* ```
*/
CallNode getAModuleLevelCall(string name) {
result = this.getModuleLevelSelf().getAMethodCall(name)
}
/** Gets a constant or `self` variable that refers to this module. */
LocalSourceNode getAnImmediateReference() {
result.asExpr().getExpr() = super.getAnImmediateReference()
}
/**
* Gets a singleton method declared in this module (or in a singleton class
* augmenting this module).
*
* Does not take inheritance into account.
*/
MethodNode getAnOwnSingletonMethod() {
result.asCallableAstNode() = super.getAnOwnSingletonMethod()
}
/**
* Gets the singleton method named `name` declared in this module (or in a singleton class
* augmenting this module).
*
* Does not take inheritance into account.
*/
MethodNode getOwnSingletonMethod(string name) {
result = this.getAnOwnSingletonMethod() and
result.getMethodName() = name
}
/**
* Gets an instance method declared in this module.
*
* Does not take inheritance into account.
*/
MethodNode getAnOwnInstanceMethod() {
result.asCallableAstNode() = this.getADeclaration().getAMethod().(Method)
}
/**
* Gets an instance method named `name` declared in this module.
*
* Does not take inheritance into account.
*/
MethodNode getOwnInstanceMethod(string name) {
result = this.getAnOwnInstanceMethod() and
result.getMethodName() = name
}
/**
* Gets the `self` parameter of an instance method declared in this module.
*
* Does not take inheritance into account.
*/
ParameterNode getAnOwnInstanceSelf() {
result = TSelfParameterNode(this.getAnOwnInstanceMethod().asCallableAstNode())
}
/**
* Gets the `self` parameter of an instance method available in this module,
* including those inherited from ancestors.
*/
ParameterNode getAnInstanceSelf() {
// Make sure to include the 'self' in overridden instance methods
result = this.getAnAncestor().getAnOwnInstanceSelf()
}
private InstanceVariableAccess getAnOwnInstanceVariableAccess(string name) {
exists(InstanceVariable v |
v.getDeclaringScope() = this.getADeclaration() and
v.getName() = name and
result.getVariable() = v
)
}
/**
* Gets an access to the instance variable `name` in this module.
*
* Does not take inheritance into account.
*/
LocalSourceNode getAnOwnInstanceVariableRead(string name) {
result.asExpr().getExpr() =
this.getAnOwnInstanceVariableAccess(name).(InstanceVariableReadAccess)
}
/**
* Gets the right-hand side of an assignment to the instance variable `name` in this module.
*
* Does not take inheritance into account.
*/
Node getAnOwnInstanceVariableWriteValue(string name) {
exists(AssignExpr assignment |
assignment.getLeftOperand() = this.getAnOwnInstanceVariableAccess(name) and
result.asExpr().getExpr() = assignment.getRightOperand()
)
}
/**
* Gets the instance method named `name` available in this module, including methods inherited
* from ancestors.
*
* Overridden methods are not included.
*/
MethodNode getInstanceMethod(string name) {
result.asCallableAstNode() = super.getInstanceMethod(name)
}
/**
* Gets an instance method available in this module, including methods inherited
* from ancestors.
*
* Overridden methods are not included.
*/
MethodNode getAnInstanceMethod() { result = this.getInstanceMethod(_) }
/**
* Gets the enclosing module, as it appears in the qualified name of this module.
*
* For example, the canonical enclosing module of `A::B` is `A`, and `A` itself has no canonical enclosing module.
*/
ModuleNode getCanonicalEnclosingModule() { result = super.getParentModule() }
/**
* Gets a module named `name` declared inside this one (not aliased), provided
* that such a module is defined or reopened in the current codebase.
*
* For example, for `A::B` the canonical nested module named `C` would be `A::B::C`.
*
* Note that this is not the same as constant lookup. If `A::B::C` would resolve to a
* module whose qualified name is not `A::B::C`, then it will not be found by
* this predicate.
*/
ModuleNode getCanonicalNestedModule(string name) { result = super.getNestedModule(name) }
}
/**
* A representation of a run-time class.
*/
class ClassNode extends ModuleNode {
ClassNode() { this.isClass() }
}
/**
* A data flow node corresponding to a method, block, or lambda expression.
*/
class CallableNode extends ExprNode {
private Callable callable;
CallableNode() { this.asExpr().getExpr() = callable }
/** Gets the underlying AST node as a `Callable`. */
Callable asCallableAstNode() { result = callable }
private ParameterPosition getParameterPosition(ParameterNodeImpl node) {
node.isSourceParameterOf(callable, result)
}
/** Gets the `n`th positional parameter. */
ParameterNode getParameter(int n) { this.getParameterPosition(result).isPositional(n) }
/** Gets the keyword parameter of the given name. */
ParameterNode getKeywordParameter(string name) {
this.getParameterPosition(result).isKeyword(name)
}
/** Gets the `self` parameter of this callable, if any. */
ParameterNode getSelfParameter() { this.getParameterPosition(result).isSelf() }
/**
* Gets the `hash-splat` parameter. This is a synthetic parameter holding
* a hash object with entries for each keyword argument passed to the function.
*/
ParameterNode getHashSplatParameter() { this.getParameterPosition(result).isHashSplat() }
/**
* Gets the block parameter of this method, if any.
*/
ParameterNode getBlockParameter() { this.getParameterPosition(result).isBlock() }
/**
* Gets a `yield` in this method or `.call` on the block parameter.
*/
CallNode getABlockCall() {
hasYieldCall(this.getBlockParameter(), result)
or
result = this.getBlockParameter().getAMethodCall("call")
}
/**
* Gets the canonical return node from this callable.
*
* Each callable has exactly one such node, and its location may not correspond
* to any particular return site - consider using `getAReturningNode` to get nodes
* whose locations correspond to return sites.
*/
Node getReturn() { result.(SynthReturnNode).getCfgScope() = callable }
/**
* Gets a data flow node whose value is about to be returned by this callable.
*/
Node getAReturningNode() { result = this.getReturn().(SynthReturnNode).getAnInput() }
}
/**
* A data flow node corresponding to a method (possibly a singleton method).
*/
class MethodNode extends CallableNode {
MethodNode() { super.asCallableAstNode() instanceof MethodBase }
/** Gets the underlying AST node for this method. */
override MethodBase asCallableAstNode() { result = super.asCallableAstNode() }
/** Gets the name of this method. */
string getMethodName() { result = this.asCallableAstNode().getName() }
}
/**
* A data flow node corresponding to a block argument.
*/
class BlockNode extends CallableNode {
BlockNode() { super.asCallableAstNode() instanceof Block }
/** Gets the underlying AST node for this block. */
override Block asCallableAstNode() { result = super.asCallableAstNode() }
}
/**
* A representation of a pair such as `K => V` or `K: V`.
*
* Unlike most expressions, pairs do not evaluate to actual objects at runtime and their nodes
* cannot generally be expected to have meaningful data flow edges.
* This node simply provides convenient access to the key and value as data flow nodes.
*/
class PairNode extends ExprNode {
PairNode() { this.getExprNode() instanceof CfgNodes::ExprNodes::PairCfgNode }
/**
* Holds if this pair is of form `key => value` or `key: value`.
*/
predicate hasKeyAndValue(Node key, Node value) {
exists(CfgNodes::ExprNodes::PairCfgNode n |
this.getExprNode() = n and
key = TExprNode(n.getKey()) and
value = TExprNode(n.getValue())
)
}
/** Gets the key expression of this pair, such as the `K` in `K => V` or `K: V`. */
Node getKey() { this.hasKeyAndValue(result, _) }
/** Gets the value expression of this pair, such as the `V` in `K => V` or `K: V`. */
Node getValue() { this.hasKeyAndValue(_, result) }
}
/**
* A data-flow node that corresponds to a hash literal. Hash literals are desugared
* into calls to `Hash.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class HashLiteralNode extends LocalSourceNode, ExprNode {
HashLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::HashLiteralCfgNode }
/** Gets a pair in this hash literal. */
PairNode getAKeyValuePair() {
result.getExprNode() =
super.getExprNode().(CfgNodes::ExprNodes::HashLiteralCfgNode).getAKeyValuePair()
}
/** Gets the value associated with the constant `key`, if known. */
Node getElementFromKey(ConstantValue key) {
exists(ExprNode keyNode |
this.getAKeyValuePair().hasKeyAndValue(keyNode, result) and
keyNode.getConstantValue() = key
)
}
}
/**
* A data-flow node corresponding to an array literal. Array literals are desugared
* into calls to `Array.[]`, so this includes both desugared calls as well as
* explicit calls.
*/
class ArrayLiteralNode extends LocalSourceNode, ExprNode {
ArrayLiteralNode() { super.getExprNode() instanceof CfgNodes::ExprNodes::ArrayLiteralCfgNode }
/**
* Gets an element of the array.
*/
Node getAnElement() { result = this.(CallNode).getPositionalArgument(_) }
}
/**
* An access to a constant, such as `C`, `C::D`, or a class or module declaration.
*
* See `DataFlow::getConst` for usage example.
*/
class ConstRef extends LocalSourceNode {
private ConstantAccess access;
ConstRef() { this = getConstantAccessNode(access) }
/** Gets the underlying constant access AST node. */
ConstantAccess asConstantAccess() { result = access }
/** Gets the underlying module declaration, if any. */
Namespace asNamespaceDeclaration() { result = access }
/** Gets the module defined or re-opened by this constant access, if any. */
ModuleNode asModule() { result.getADeclaration() = access }
/**
* Gets the simple name of the constant being referenced, such as
* the `B` in `A::B`.
*/
string getName() { result = access.getName() }
/**
* Holds if this might refer to a top-level constant.
*/
predicate isPossiblyGlobal() {
exists(Module mod |
not exists(mod.getParentModule()) and
mod.getAnImmediateReference() = access
)
or
not exists(Module mod | mod.getAnImmediateReference() = access) and
not exists(access.getScopeExpr())
}
/**
* Gets a module for which this constant is the reference to an ancestor module.
*
* For example, `M` is the ancestry target of `C` in the following examples:
* ```rb
* class M < C {}
*
* module M
* include C
* end
*
* module M
* prepend C
* end
* ```
*/
private ModuleNode getAncestryTarget() { result.getAnAncestorExpr() = this }
/**
* Gets the known target module.
*
* We resolve these differently to prune out infeasible constant lookups.
*/
private Module getExactTarget() { result.getAnImmediateReference() = access }
/**
* Gets a scope in which a constant lookup may access the contents of the module referenced by this constant.
*/
cached
private TConstLookupScope getATargetScope() {
forceCachingInSameStage() and
result = MkAncestorLookup(this.getAncestryTarget().getAnImmediateDescendent*())
or
access = any(ConstantAccess ac).getScopeExpr() and
result = MkQualifiedLookup(access)
or
result = MkNestedLookup(this.getAncestryTarget())
or
result = MkExactLookup(access.(Namespace).getModule())
}
/**
* Gets the scope expression, or the immediately enclosing `Namespace` (skipping over singleton classes).
*
* Top-levels are not included, since this is only needed for nested constant lookup, and unqualified constants
* at the top-level are handled by `DataFlow::getConst`, never `ConstRef.getConst`.
*/
private TConstLookupScope getLookupScope() {
result = MkQualifiedLookup(access.getScopeExpr())
or
not exists(this.getExactTarget()) and
not exists(access.getScopeExpr()) and
not access.hasGlobalScope() and
(
result = MkAncestorLookup(access.getEnclosingModule().getNamespaceOrToplevel().getModule())
or
result = MkNestedLookup(access.getEnclosingModule().getEnclosingModule*().getModule())
)
}
/**
* Holds if this can reference a constant named `name` from `scope` using a lookup of `kind`.
*/
cached
private predicate accesses(TConstLookupScope scope, string name) {
forceCachingInSameStage() and
scope = this.getLookupScope() and
name = this.getName()
or
exists(Module mod |
this.getExactTarget() = mod.getNestedModule(name) and
scope = MkExactLookup(mod)
)
}
/**
* Gets a constant reference that may resolve to a member of this node.
*
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
* ```rb
* A::B # simple reference
*
* module A
* B # in scope
* module X
* B # in nested scope
* end
* end
*
* module X
* include A
* B # via inclusion
* end
*
* class X < A
* B # via subclassing
* end
* ```
*/
pragma[inline]
ConstRef getConst(string name) {
exists(TConstLookupScope scope |
pragma[only_bind_into](scope) = pragma[only_bind_out](this).getATargetScope() and
result.accesses(pragma[only_bind_out](scope), name)
)
}
/**
* Gets a module that transitively subclasses, includes, or prepends the module referred to by
* this constant.
*
* For example, `DataFlow::getConst("A").getADescendentModule()` finds `B`, `C`, and `E`:
* ```rb
* class B < A
* end
*
* class C < B
* end
*
* module E
* include C
* end
* ```
*/
ModuleNode getADescendentModule() { MkAncestorLookup(result) = this.getATargetScope() }
}
/**
* Gets a constant reference that may resolve to the top-level constant `name`.
*
* To get nested constants, call `getConst()` one or more times on the result.
*
* For example `DataFlow::getConst("A").getConst("B")` finds the following:
* ```rb
* A::B # simple reference
*
* module A
* B # in scope
* module X
* B # in nested scope
* end
* end
*
* module X
* include A
* B # via inclusion
* end
*
* class X < A
* B # via subclassing
* end
* ```
*/
pragma[nomagic]
ConstRef getConst(string name) {
result.getName() = name and
result.isPossiblyGlobal()
}

View File

@@ -115,8 +115,8 @@ private module Cached {
*/ */
cached cached
predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) { predicate localTaintStepCached(DataFlow::Node nodeFrom, DataFlow::Node nodeTo) {
defaultAdditionalTaintStep(nodeFrom, nodeTo) DataFlow::localFlowStep(nodeFrom, nodeTo) or
or defaultAdditionalTaintStep(nodeFrom, nodeTo) or
// Simple flow through library code is included in the exposed local // Simple flow through library code is included in the exposed local
// step relation, even though flow is technically inter-procedural // step relation, even though flow is technically inter-procedural
FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _) FlowSummaryImpl::Private::Steps::summaryThroughStepTaint(nodeFrom, nodeTo, _)

View File

@@ -24,7 +24,7 @@ deprecated class ParamsCall = Rails::ParamsCall;
deprecated class CookiesCall = Rails::CookiesCall; deprecated class CookiesCall = Rails::CookiesCall;
/** /**
* A `ClassDeclaration` for a class that extends `ActionController::Base`. * A class that extends `ActionController::Base`.
* For example, * For example,
* *
* ```rb * ```rb
@@ -36,26 +36,61 @@ deprecated class CookiesCall = Rails::CookiesCall;
* end * end
* ``` * ```
*/ */
class ActionControllerControllerClass extends ClassDeclaration { class ActionControllerClass extends DataFlow::ClassNode {
ActionControllerControllerClass() { ActionControllerClass() {
this.getSuperclassExpr() = this =
[ [
API::getTopLevelMember("ActionController").getMember("Base"), DataFlow::getConst("ActionController").getConst("Base"),
// In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we // In Rails applications `ApplicationController` typically extends `ActionController::Base`, but we
// treat it separately in case the `ApplicationController` definition is not in the database. // treat it separately in case the `ApplicationController` definition is not in the database.
API::getTopLevelMember("ApplicationController"), DataFlow::getConst("ApplicationController"),
// ActionController::Metal technically doesn't contain all of the // ActionController::Metal technically doesn't contain all of the
// methods available in Base, such as those for rendering views. // methods available in Base, such as those for rendering views.
// However we prefer to be over-sensitive in this case in order to find // However we prefer to be over-sensitive in this case in order to find
// more results. // more results.
API::getTopLevelMember("ActionController").getMember("Metal") DataFlow::getConst("ActionController").getConst("Metal")
].getASubclass().getAValueReachableFromSource().asExpr().getExpr() ].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. * 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. * This may be the target of a route handler, if such a route is defined.
*/ */
class ActionControllerActionMethod extends Method, Http::Server::RequestHandler::Range { 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 * 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. * Gets the controller class containing this method.
*/ */
ActionControllerControllerClass getControllerClass() { ActionControllerClass getControllerClass() {
// TODO: model the implicit render call when a path through the method does // TODO: model the implicit render call when a path through the method does
// not end at an explicit render or redirect // not end at an explicit render or redirect
result = controllerClass result = controllerClass
@@ -102,37 +139,23 @@ class ActionControllerActionMethod extends Method, Http::Server::RequestHandler:
* May return multiple results. * May return multiple results.
*/ */
ActionDispatch::Routing::Route getARoute() { ActionDispatch::Routing::Route getARoute() {
exists(string name | exists(string name, DataFlow::MethodNode m |
isRoute(result, name, controllerClass) and isRoute(result, name, controllerClass) and
isActionControllerMethod(this, name, controllerClass) m = controllerClass.getInstanceMethod(name) and
this = m.asCallableAstNode()
) )
} }
} }
pragma[nomagic] pragma[nomagic]
private predicate isRoute( private predicate isRoute(
ActionDispatch::Routing::Route route, string name, ActionControllerControllerClass controllerClass ActionDispatch::Routing::Route route, string name, ActionControllerClass controllerClass
) { ) {
route.getController() + "_controller" = route.getController() + "_controller" =
ActionDispatch::Routing::underscore(controllerClass.getAQualifiedName()) and ActionDispatch::Routing::underscore(controllerClass.getQualifiedName()) and
name = route.getAction() 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 * A `RemoteFlowSource::Range` to represent accessing the
* ActionController parameters available via the `params` method. * 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. */ /** A call to `cookies` from within a controller. */
private class ActionControllerCookiesCall extends ActionControllerContextCall, CookiesCallImpl { private class ActionControllerCookiesCall extends CookiesCallImpl {
ActionControllerCookiesCall() { this.getMethodName() = "cookies" } ActionControllerCookiesCall() {
this = actionControllerInstance().getAMethodCall("cookies").asExpr().getExpr()
}
} }
/** A call to `params` from within a controller. */ /** A call to `params` from within a controller. */
private class ActionControllerParamsCall extends ActionControllerContextCall, ParamsCallImpl { private class ActionControllerParamsCall extends ParamsCallImpl {
ActionControllerParamsCall() { this.getMethodName() = "params" } ActionControllerParamsCall() {
this = actionControllerInstance().getAMethodCall("params").asExpr().getExpr()
}
} }
/** Modeling for `ActionDispatch::Request`. */ /** Modeling for `ActionDispatch::Request`. */
@@ -174,10 +201,7 @@ private module Request {
* `ActionDispatch::Request`. * `ActionDispatch::Request`.
*/ */
private class RequestNode extends DataFlow::CallNode { private class RequestNode extends DataFlow::CallNode {
RequestNode() { RequestNode() { this = actionControllerInstance().getAMethodCall("request") }
this.asExpr().getExpr() instanceof ActionControllerContextCall and
this.getMethodName() = "request"
}
} }
/** /**
@@ -234,7 +258,7 @@ private module Request {
// Request headers are prefixed with `HTTP_` to distinguish them from // Request headers are prefixed with `HTTP_` to distinguish them from
// "headers" supplied by Rack middleware. // "headers" supplied by Rack middleware.
this.getMethodName() = ["get_header", "fetch_header"] and 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() } 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 { private class EnvHttpAccess extends DataFlow::CallNode, Http::Server::RequestInputAccess::Range {
EnvHttpAccess() { EnvHttpAccess() {
any(EnvCall c).(DataFlow::LocalSourceNode).flowsTo(this.getReceiver()) and this = any(EnvCall c).getAMethodCall("[]") and
this.getMethodName() = "[]" and this.getArgument(0).getConstantValue().getString().regexpMatch("^HTTP_.+")
this.getArgument(0).asExpr().getExpr().getConstantValue().getString().regexpMatch("^HTTP_.+")
} }
override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() } override Http::Server::RequestInputKind getKind() { result = Http::Server::headerInputKind() }
@@ -302,21 +325,32 @@ private module Request {
} }
/** A call to `render` from within a controller. */ /** A call to `render` from within a controller. */
private class ActionControllerRenderCall extends ActionControllerContextCall, RenderCallImpl { private class ActionControllerRenderCall extends RenderCallImpl {
ActionControllerRenderCall() { this.getMethodName() = "render" } ActionControllerRenderCall() {
this = actionControllerInstance().getAMethodCall("render").asExpr().getExpr()
}
} }
/** A call to `render_to` from within a controller. */ /** A call to `render_to` from within a controller. */
private class ActionControllerRenderToCall extends ActionControllerContextCall, RenderToCallImpl { private class ActionControllerRenderToCall extends RenderToCallImpl {
ActionControllerRenderToCall() { this.getMethodName() = ["render_to_body", "render_to_string"] } ActionControllerRenderToCall() {
this =
actionControllerInstance()
.getAMethodCall(["render_to_body", "render_to_string"])
.asExpr()
.getExpr()
}
} }
/** A call to `html_escape` from within a controller. */ /** A call to `html_escape` from within a controller. */
private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl { private class ActionControllerHtmlEscapeCall extends HtmlEscapeCallImpl {
ActionControllerHtmlEscapeCall() { ActionControllerHtmlEscapeCall() {
// "h" is aliased to "html_escape" in ActiveSupport // "h" is aliased to "html_escape" in ActiveSupport
this.getMethodName() = ["html_escape", "html_escape_once", "h", "sanitize"] and this =
this.getEnclosingModule() instanceof ActionControllerControllerClass 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 * 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. * specific URL/path or to a different action in this controller.
*/ */
class RedirectToCall extends ActionControllerContextCall { class RedirectToCall extends MethodCall {
private ActionControllerClass controller;
RedirectToCall() { 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 */ /** 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 */ /** Gets the `ActionControllerActionMethod` to redirect to, if any */
ActionControllerActionMethod getRedirectActionMethod() { ActionControllerActionMethod getRedirectActionMethod() {
this.getKeywordArgument("action").getConstantValue().isStringlikeValue(result.getName()) and exists(string name |
result.getEnclosingModule() = this.getControllerClass() this.getKeywordArgument("action").getConstantValue().isStringlikeValue(name) and
result = controller.getInstanceMethod(name).asCallableAstNode()
)
} }
/** /**
@@ -373,18 +416,8 @@ class ActionControllerRedirectResponse extends Http::Server::HttpRedirectRespons
} }
pragma[nomagic] pragma[nomagic]
private predicate isActionControllerMethod(Method m, string name, ActionControllerControllerClass c) { private predicate actionControllerHasHelperMethodCall(ActionControllerClass c, string name) {
m.getName() = name and c.getAModuleLevelCall("helper_method").getArgument(_).getConstantValue().isStringlikeValue(name)
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
)
} }
/** /**
@@ -404,27 +437,28 @@ private predicate actionControllerHasHelperMethodCall(ActionControllerController
* See also https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method * See also https://api.rubyonrails.org/classes/AbstractController/Helpers/ClassMethods.html#method-i-helper_method
*/ */
class ActionControllerHelperMethod extends Method { class ActionControllerHelperMethod extends Method {
private ActionControllerControllerClass controllerClass; private ActionControllerClass controllerClass;
ActionControllerHelperMethod() { ActionControllerHelperMethod() {
exists(string name | exists(DataFlow::MethodNode m, string name |
isActionControllerMethod(this, name, controllerClass) and m = controllerClass.getInstanceMethod(name) and
actionControllerHasHelperMethodCall(controllerClass, name) actionControllerHasHelperMethodCall(controllerClass, name) and
this = m.asCallableAstNode()
) )
} }
/** Gets the class containing this helper method. */ /** 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. * according to Rails path conventions.
* For instance, a template file at `app/views/foo/bar/baz.html.erb` will be * 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`, * mapped to a controller class in `app/controllers/foo/bar/baz_controller.rb`,
* if such a controller class exists. * 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 // There is a direct mapping from template file to controller class
controllerTemplateFile(result, f) controllerTemplateFile(result, f)
or or
@@ -442,13 +476,13 @@ ActionControllerControllerClass getAssociatedControllerClass(ErbFile f) {
// TODO: improve layout support, e.g. for `layout` method // TODO: improve layout support, e.g. for `layout` method
// https://guides.rubyonrails.org/layouts_and_rendering.html // 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. * `ActionControllerControllerClass`, according to Rails conventions.
* *
* This handles mappings between controllers in `app/controllers/`, and * This handles mappings between controllers in `app/controllers/`, and
* templates in `app/views/` and `app/views/layouts/`. * 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 | exists(string templatesPath, string sourcePrefix, string subPath, string controllerPath |
controllerPath = cls.getLocation().getFile().getRelativePath() and controllerPath = cls.getLocation().getFile().getRelativePath() and
templatesPath = templateFile.getParentContainer().getRelativePath() and templatesPath = templateFile.getParentContainer().getRelativePath() and
@@ -484,16 +518,14 @@ class ActionControllerSkipForgeryProtectionCall extends CsrfProtectionSetting::R
/** /**
* A call to `protect_from_forgery`. * A call to `protect_from_forgery`.
*/ */
private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range { private class ActionControllerProtectFromForgeryCall extends CsrfProtectionSetting::Range,
private ActionControllerContextCall callExpr; DataFlow::CallNode {
ActionControllerProtectFromForgeryCall() { ActionControllerProtectFromForgeryCall() {
callExpr = this.asExpr().getExpr() and this = actionControllerInstance().getAMethodCall("protect_from_forgery")
callExpr.getMethodName() = "protect_from_forgery"
} }
private string getWithValueText() { 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 // 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 { private class SendFile extends FileSystemAccess::Range, DataFlow::CallNode {
SendFile() { SendFile() {
this.getMethodName() = "send_file" and this = [actionControllerInstance(), Response::response()].getAMethodCall("send_file")
(
this.asExpr().getExpr() instanceof ActionControllerContextCall or
this.getReceiver().asExpr().getExpr() instanceof Response::ResponseCall
)
} }
override DataFlow::Node getAPathArgument() { result = this.getArgument(0) } override DataFlow::Node getAPathArgument() { result = this.getArgument(0) }
@@ -522,21 +550,13 @@ private module ParamsSummaries {
private import codeql.ruby.dataflow.FlowSummary 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. * from method calls on other instances.
*/ */
private class ParamsInstance extends DataFlow::Node { private DataFlow::LocalSourceNode paramsInstance() {
ParamsInstance() { result.asExpr().getExpr() instanceof Rails::ParamsCall
this.asExpr().getExpr() instanceof Rails::ParamsCall or
or result = paramsInstance().getAMethodCall(paramsMethodReturningParamsInstance())
this =
any(DataFlow::CallNode call |
call.getReceiver() instanceof ParamsInstance and
call.getMethodName() = paramsMethodReturningParamsInstance()
)
or
exists(ParamsInstance prev | prev.(DataFlow::LocalSourceNode).flowsTo(this))
}
} }
/** /**
@@ -578,8 +598,7 @@ private module ParamsSummaries {
MethodsReturningParamsInstanceSummary() { this = "ActionController::Parameters#<various>" } MethodsReturningParamsInstanceSummary() { this = "ActionController::Parameters#<various>" }
override MethodCall getACall() { override MethodCall getACall() {
any(ParamsInstance i).asExpr().getExpr() = result.getReceiver() and result = paramsInstance().getAMethodCall(methodReturnsTaintFromSelf()).asExpr().getExpr()
result.getMethodName() = methodReturnsTaintFromSelf()
} }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -601,9 +620,8 @@ private module ParamsSummaries {
override MethodCall getACall() { override MethodCall getACall() {
result.getMethodName() = ["merge", "reverse_merge", "with_defaults"] and result.getMethodName() = ["merge", "reverse_merge", "with_defaults"] and
exists(ParamsInstance i | paramsInstance().getALocalUse().asExpr().getExpr() =
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)] [result.getReceiver(), result.getArgument(0)]
)
} }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -625,9 +643,8 @@ private module ParamsSummaries {
override MethodCall getACall() { override MethodCall getACall() {
result.getMethodName() = ["merge!", "reverse_merge!", "with_defaults!"] and result.getMethodName() = ["merge!", "reverse_merge!", "with_defaults!"] and
exists(ParamsInstance i | paramsInstance().getALocalUse().asExpr().getExpr() =
i.asExpr().getExpr() = [result.getReceiver(), result.getArgument(0)] [result.getReceiver(), result.getArgument(0)]
)
} }
override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { override predicate propagatesFlowExt(string input, string output, boolean preservesValue) {
@@ -643,15 +660,12 @@ private module ParamsSummaries {
* response. * response.
*/ */
private module Response { private module Response {
class ResponseCall extends ActionControllerContextCall { DataFlow::LocalSourceNode response() {
ResponseCall() { this.getMethodName() = "response" } result = actionControllerInstance().getAMethodCall("response")
} }
class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range { class BodyWrite extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
BodyWrite() { BodyWrite() { this = response().getAMethodCall("body=") }
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "body="
}
override DataFlow::Node getBody() { result = this.getArgument(0) } override DataFlow::Node getBody() { result = this.getArgument(0) }
@@ -661,10 +675,7 @@ private module Response {
} }
class SendFileCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range { class SendFileCall extends DataFlow::CallNode, Http::Server::HttpResponse::Range {
SendFileCall() { SendFileCall() { this = response().getAMethodCall("send_file") }
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and
this.getMethodName() = "send_file"
}
override DataFlow::Node getBody() { result = this.getArgument(0) } override DataFlow::Node getBody() { result = this.getArgument(0) }
@@ -677,19 +688,12 @@ private module Response {
HeaderWrite() { HeaderWrite() {
// response.header[key] = val // response.header[key] = val
// response.headers[key] = val // response.headers[key] = val
exists(MethodCall headerCall | this = response().getAMethodCall(["header", "headers"]).getAMethodCall("[]=")
headerCall.getMethodName() = ["header", "headers"] and
headerCall.getReceiver() instanceof ResponseCall
|
this.getReceiver().asExpr().getExpr() = headerCall and
this.getMethodName() = "[]="
)
or or
// response.set_header(key) = val // response.set_header(key) = val
// response[header] = val // response[header] = val
// response.add_header(key, val) // response.add_header(key, val)
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and this = response().getAMethodCall(["set_header", "[]=", "add_header"])
this.getMethodName() = ["set_header", "[]=", "add_header"]
} }
override string getName() { override string getName() {
@@ -702,12 +706,12 @@ private module Response {
class SpecificHeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range { class SpecificHeaderWrite extends DataFlow::CallNode, Http::Server::HeaderWriteAccess::Range {
SpecificHeaderWrite() { SpecificHeaderWrite() {
// response.<method> = val // response.<method> = val
this.getReceiver().asExpr().getExpr() instanceof ResponseCall and this =
this.getMethodName() = response()
[ .getAMethodCall([
"location=", "cache_control=", "_cache_control=", "etag=", "charset=", "content_type=", "location=", "cache_control=", "_cache_control=", "etag=", "charset=",
"date=", "last_modified=", "weak_etag=", "strong_etag=" "content_type=", "date=", "last_modified=", "weak_etag=", "strong_etag="
] ])
} }
override string getName() { override string getName() {

View File

@@ -571,9 +571,7 @@ class ActiveRecordAssociation extends DataFlow::CallNode {
* For example, in `has_many :posts`, this is `post`. * For example, in `has_many :posts`, this is `post`.
*/ */
string getTargetModelName() { string getTargetModelName() {
exists(string s | exists(string s | s = this.getArgument(0).getConstantValue().getStringlikeValue() |
s = this.getArgument(0).asExpr().getExpr().getConstantValue().getStringlikeValue()
|
// has_one :profile // has_one :profile
// belongs_to :user // belongs_to :user
this.isSingular() and this.isSingular() and

View File

@@ -182,3 +182,39 @@ module FileUtils {
override DataFlow::Node getAPermissionNode() { result = permissionArg } override DataFlow::Node getAPermissionNode() { result = permissionArg }
} }
} }
/**
* Classes and predicates for modeling the core `Dir` module.
*/
module Dir {
/**
* A call to a method on `Dir` that operates on a path as its first argument, and produces file-names.
* Considered as a `FileNameSource` and a `FileSystemAccess`.
*/
class DirGlob extends FileSystemAccess::Range, FileNameSource instanceof DataFlow::CallNode {
DirGlob() {
this =
API::getTopLevelMember("Dir")
.getAMethodCall(["glob", "[]", "children", "each_child", "entries", "foreach"])
}
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
}
/**
* A call to a method on `Dir` that operates on a path as its first argument, considered as a `FileSystemAccess`.
*/
class DirPathAccess extends FileSystemAccess::Range instanceof DataFlow::CallNode {
DirPathAccess() {
this =
API::getTopLevelMember("Dir")
.getAMethodCall([
"chdir", "chroot", "delete", "empty?", "exist?", "exists?", "mkdir", "new", "open",
"rmdir", "unlink"
])
}
override DataFlow::Node getAPathArgument() { result = super.getArgument(0) }
}
// TODO: Model that `(Dir.new "foo").each { |f| ... }` yields a filename (and some other public methods)
}

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::getConst("Rails") }
/**
* Gets a reference to either `Rails::Railtie`, `Rails::Engine`, or `Rails::Application`.
* `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database. * `Engine` and `Application` extend `Railtie`, but may not have definitions present in the database.
*/ */
private class RailtieClassAccess extends ConstantReadAccess { private DataFlow::ConstRef railtie() {
RailtieClassAccess() { result = rails().getConst(["Railtie", "Engine", "Application"])
this.getScopeExpr().(ConstantAccess).getName() = "Rails" and
this.getName() = ["Railtie", "Engine", "Application"]
}
} }
// A `ClassDeclaration` that (transitively) extends `Rails::Railtie` /** Gets a class that transitively extends `Rails::Railtie` */
private class RailtieClass extends ClassDeclaration { private DataFlow::ClassNode railtieClass() { result = railtie().getADescendentModule() }
RailtieClass() {
this.getSuperclassExpr() instanceof RailtieClassAccess or
exists(RailtieClass other |
other.getModule() = this.getSuperclassExpr().(ConstantReadAccess).getModule()
)
}
}
private DataFlow::CallNode getAConfigureCallNode() { /**
// `Rails.application.configure` * Gets a reference to `Rails::Application` or `Rails.application`.
result = API::getTopLevelMember("Rails").getReturn("application").getAMethodCall("configure") */
private DataFlow::LocalSourceNode railsApp() {
result = rails().getAMethodCall("application")
or or
// `Rails::Application.configure` result = rails().getConst("Application")
exists(ConstantReadAccess read, RailtieClass cls |
read = result.getReceiver().asExpr().getExpr() and
read.getModule() = cls.getModule() and
result.asExpr().getExpr().(MethodCall).getMethodName() = "configure"
)
} }
/** /**
* Classes representing accesses to the Rails config object. * Classes representing accesses to the Rails config object.
*/ */
private module Config { private module Config {
/** DataFlow::LocalSourceNode configSource() {
* An access to a Rails config object. // `Foo < Rails::Application ... config ...`
*/ result = railtieClass().getAnOwnModuleSelf().getAMethodCall("config")
private class SourceNode extends DataFlow::LocalSourceNode { or
SourceNode() { // `Rails.application.config`
// `Foo < Rails::Application ... config ...` result = railsApp().getAMethodCall("config")
exists(MethodCall configCall | this.asExpr().getExpr() = configCall | or
configCall.getMethodName() = "config" and // TODO: move away from getParent+() when have better infrastructure for overridden 'self' in blocks
configCall.getEnclosingModule() instanceof RailtieClass // `Rails.application.configure { ... config ... }`
) // `Rails::Application.configure { ... config ... }`
or exists(Block block, MethodCall configCall | configCall = result.asExpr().getExpr() |
// `Rails.application.config` block = railsApp().getAMethodCall("configure").getBlock().asExpr().getExpr() and
this = API::getTopLevelMember("Rails").getReturn("application").getReturn("config").asSource() configCall.getParent+() = block and
or configCall.getMethodName() = "config"
// `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"
)
}
} }
/** /**
* A reference to the Rails config object. * Gets a reference to the ActionController config object.
*/ */
class Node extends DataFlow::Node { DataFlow::LocalSourceNode actionController() {
Node() { exists(SourceNode src | src.flowsTo(this)) } 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 { DataFlow::LocalSourceNode actionDispatch() {
ActionControllerNode() { result = configSource().getAMethodCall("action_dispatch")
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)
)
}
} }
} }
@@ -200,24 +161,18 @@ private module Settings {
loc.getFile().getStem() = "test" loc.getFile().getStem() = "test"
} }
private class Setting extends DataFlow::CallNode { private class Setting extends DataFlow::SetterCallNode {
Setting() { Setting() {
// exclude some test configuration // exclude some test configuration
not isInTestConfiguration(this.getLocation()) and not isInTestConfiguration(this.getLocation()) and
this.getReceiver+() instanceof Config::Node and this = Config::configSource().getAMethodCall+()
this.asExpr().getExpr() instanceof SetterMethodCall
} }
} }
private class LiteralSetting extends Setting { private class LiteralSetting extends Setting {
ConstantValue value; ConstantValue value;
LiteralSetting() { LiteralSetting() { value = this.getArgument(0).getALocalSource().getConstantValue() }
exists(DataFlow::LocalSourceNode lsn |
lsn.asExpr().getConstantValue() = value and
lsn.flowsTo(this.getArgument(0))
)
}
string getValueText() { result = value.toString() } string getValueText() { result = value.toString() }
@@ -262,8 +217,7 @@ private module Settings {
private class AllowForgeryProtectionSetting extends Settings::BooleanSetting, private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
CsrfProtectionSetting::Range { CsrfProtectionSetting::Range {
AllowForgeryProtectionSetting() { AllowForgeryProtectionSetting() {
this.getReceiver() instanceof Config::ActionControllerNode and this = Config::actionController().getAMethodCall("allow_forgery_protection=")
this.getMethodName() = "allow_forgery_protection="
} }
override boolean getVerificationSetting() { result = this.getValue() } override boolean getVerificationSetting() { result = this.getValue() }
@@ -277,8 +231,7 @@ private class AllowForgeryProtectionSetting extends Settings::BooleanSetting,
private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting, private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
CookieSecurityConfigurationSetting::Range { CookieSecurityConfigurationSetting::Range {
EncryptedCookieCipherSetting() { EncryptedCookieCipherSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and this = Config::actionDispatch().getAMethodCall("encrypted_cookie_cipher=")
this.getMethodName() = "encrypted_cookie_cipher="
} }
OpenSslCipher getCipher() { this.getValueText() = result.getName() } OpenSslCipher getCipher() { this.getValueText() = result.getName() }
@@ -298,8 +251,7 @@ private class EncryptedCookieCipherSetting extends Settings::StringlikeSetting,
private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanSetting, private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanSetting,
CookieSecurityConfigurationSetting::Range { CookieSecurityConfigurationSetting::Range {
UseAuthenticatedCookieEncryptionSetting() { UseAuthenticatedCookieEncryptionSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and this = Config::actionDispatch().getAMethodCall("use_authenticated_cookie_encryption=")
this.getMethodName() = "use_authenticated_cookie_encryption="
} }
boolean getDefaultValue() { result = true } boolean getDefaultValue() { result = true }
@@ -321,8 +273,7 @@ private class UseAuthenticatedCookieEncryptionSetting extends Settings::BooleanS
private class CookiesSameSiteProtectionSetting extends Settings::NillableStringlikeSetting, private class CookiesSameSiteProtectionSetting extends Settings::NillableStringlikeSetting,
CookieSecurityConfigurationSetting::Range { CookieSecurityConfigurationSetting::Range {
CookiesSameSiteProtectionSetting() { CookiesSameSiteProtectionSetting() {
this.getReceiver() instanceof Config::ActionDispatchNode and this = Config::actionDispatch().getAMethodCall("cookies_same_site_protection=")
this.getMethodName() = "cookies_same_site_protection="
} }
string getDefaultValue() { result = "lax" } string getDefaultValue() { result = "lax" }

View File

@@ -12,26 +12,15 @@ private import codeql.ruby.DataFlow
* Modeling for `railties`. * Modeling for `railties`.
*/ */
module Railties { module Railties {
private class IncludeOrPrependCall extends MethodCall { private DataFlow::ConstRef generatorsActionsConst() {
IncludeOrPrependCall() { this.getMethodName() = ["include", "prepend"] } result = DataFlow::getConst("Rails").getConst("Generators").getConst("Actions")
} }
/** /**
* A class which `include`s `Rails::Generators::Actions`. * Gets a class which is a descendent of `Rails::Generators::Actions`.
*/ */
private class GeneratorsActionsContext extends ClassDeclaration { private DataFlow::ClassNode generatorsActionsClass() {
GeneratorsActionsContext() { result = generatorsActionsConst().getADescendentModule()
exists(IncludeOrPrependCall i |
i.getEnclosingModule() = this and
i.getArgument(0) =
API::getTopLevelMember("Rails")
.getMember("Generators")
.getMember("Actions")
.getAValueReachableFromSource()
.asExpr()
.getExpr()
)
}
} }
/** /**
@@ -40,8 +29,7 @@ module Railties {
*/ */
private class ExecuteCommandCall extends SystemCommandExecution::Range, DataFlow::CallNode { private class ExecuteCommandCall extends SystemCommandExecution::Range, DataFlow::CallNode {
ExecuteCommandCall() { ExecuteCommandCall() {
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and this = generatorsActionsClass().getAnInstanceSelf().getAMethodCall("execute_command")
this.getMethodName() = "execute_command"
} }
override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) } override DataFlow::Node getAnArgument() { result = this.getArgument([0, 1]) }
@@ -54,8 +42,10 @@ module Railties {
*/ */
private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range, DataFlow::CallNode { private class ExecuteCommandWrapperCall extends SystemCommandExecution::Range, DataFlow::CallNode {
ExecuteCommandWrapperCall() { ExecuteCommandWrapperCall() {
this.asExpr().getExpr().getEnclosingModule() instanceof GeneratorsActionsContext and this =
this.getMethodName() = ["rake", "rails_command", "git"] generatorsActionsClass()
.getAnInstanceSelf()
.getAMethodCall(["rake", "rails_command", "git"])
} }
override DataFlow::Node getAnArgument() { result = this.getArgument(0) } 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) { private predicate isOjModePair(CfgNodes::ExprNodes::PairCfgNode p, string modeValue) {
p.getKey().getConstantValue().isStringlikeValue("mode") and p.getKey().getConstantValue().isStringlikeValue("mode") and
exists(DataFlow::LocalSourceNode symbolLiteral, DataFlow::Node value | DataFlow::exprNode(p.getValue()).getALocalSource().getConstantValue().isSymbol(modeValue)
symbolLiteral.asExpr().getExpr().getConstantValue().isSymbol(modeValue) and
symbolLiteral.flowsTo(value) and
value.asExpr() = p.getValue()
)
} }
/** /**

View File

@@ -180,11 +180,10 @@ private module Shared {
private predicate isFlowFromLocals0( private predicate isFlowFromLocals0(
CfgNodes::ExprNodes::ElementReferenceCfgNode refNode, string hashKey, ErbFile erb 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 argNode.asExpr() = refNode.getArgument(0) and
refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and refNode.getReceiver().getExpr().(MethodCall).getMethodName() = "local_assigns" and
argNode.getALocalSource() = DataFlow::exprNode(strNode) and argNode.getALocalSource().getConstantValue().isStringlikeValue(hashKey) and
strNode.getExpr().getConstantValue().isStringlikeValue(hashKey) and
erb = refNode.getFile() erb = refNode.getFile()
) )
} }

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-all name: codeql/ruby-all
version: 0.4.2 version: 0.4.3-dev
groups: ruby groups: ruby
extractor: ruby extractor: ruby
dbscheme: ruby.dbscheme dbscheme: ruby.dbscheme

View File

@@ -17,18 +17,11 @@ import codeql.ruby.frameworks.ActionController
import DataFlow::PathGraph 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. * This probably refers to the incoming HTTP request object.
*/ */
class ActionControllerRequest extends DataFlow::Node { DataFlow::LocalSourceNode request() {
ActionControllerRequest() { result = any(ActionControllerClass cls).getSelf().getAMethodCall("request")
exists(DataFlow::CallNode c |
c.asExpr().getExpr().getEnclosingModule() instanceof ActionControllerControllerClass and
c.getMethodName() = "request"
|
c.flowsTo(this)
)
}
} }
/** /**
@@ -36,9 +29,11 @@ class ActionControllerRequest extends DataFlow::Node {
*/ */
class WeakParams extends DataFlow::CallNode { class WeakParams extends DataFlow::CallNode {
WeakParams() { WeakParams() {
this.getReceiver() instanceof ActionControllerRequest and this =
this.getMethodName() = request()
["path_parameters", "query_parameters", "request_parameters", "GET", "POST"] .getAMethodCall([
"path_parameters", "query_parameters", "request_parameters", "GET", "POST"
])
} }
} }

View File

@@ -1,5 +1,5 @@
name: codeql/ruby-queries name: codeql/ruby-queries
version: 0.4.2 version: 0.4.3-dev
groups: groups:
- ruby - ruby
- queries - queries

View File

@@ -20,7 +20,7 @@ import codeql.ruby.ast.Literal
from AmbiguousPathCall call from AmbiguousPathCall call
where where
// there is not a constant string argument // 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 // if it's a format string, then the first argument is not a constant string
not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0) not call.getPathArgument().getALocalSource().asExpr().getExpr().(StringLiteral).getComponent(0)
instanceof StringTextComponent instanceof StringTextComponent

View File

@@ -2833,6 +2833,29 @@ operations/operations.rb:
# 96| getStmt: [AssignMulExpr] ... *= ... # 96| getStmt: [AssignMulExpr] ... *= ...
# 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var # 96| getAnOperand/getLeftOperand: [GlobalVariableAccess] $global_var
# 96| getAnOperand/getRightOperand: [IntegerLiteral] 6 # 96| getAnOperand/getRightOperand: [IntegerLiteral] 6
# 98| getStmt: [AssignExpr] ... = ...
# 98| getAnOperand/getLeftOperand: [ConstantAssignment] CONSTANT1
# 98| getAnOperand/getRightOperand: [IntegerLiteral] 5
# 99| getStmt: [AssignAddExpr] ... += ...
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getRightOperand: [IntegerLiteral] 6
# 100| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getRightOperand: [IntegerLiteral] 7
# 101| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 101| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] MemberConstant
# 101| getScopeExpr: [ConstantReadAccess] Foo
# 101| getAnOperand/getRightOperand: [IntegerLiteral] 8
# 102| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 102| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] OtherConstant
# 102| getScopeExpr: [MethodCall] call to bar
# 102| getReceiver: [MethodCall] call to foo
# 102| getReceiver: [SelfVariableAccess] self
# 102| getArgument: [IntegerLiteral] 1
# 102| getAnOperand/getRightOperand: [IntegerLiteral] 7
# 103| getStmt: [AssignLogicalOrExpr] ... ||= ...
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getRightOperand: [IntegerLiteral] 7
params/params.rb: params/params.rb:
# 1| [Toplevel] params.rb # 1| [Toplevel] params.rb
# 4| getStmt: [Method] identifier_method_params # 4| getStmt: [Method] identifier_method_params

View File

@@ -865,6 +865,51 @@ operations/operations.rb:
# 96| getAnOperand/getRightOperand: [MulExpr] ... * ... # 96| getAnOperand/getRightOperand: [MulExpr] ... * ...
# 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var # 96| getAnOperand/getLeftOperand/getReceiver: [GlobalVariableAccess] $global_var
# 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6 # 96| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
# 99| [AssignAddExpr] ... += ...
# 99| getDesugared: [AssignExpr] ... = ...
# 99| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getRightOperand: [AddExpr] ... + ...
# 99| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT2
# 99| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 6
# 100| [AssignLogicalOrExpr] ... ||= ...
# 100| getDesugared: [AssignExpr] ... = ...
# 100| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 100| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT3
# 100| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
# 101| [AssignLogicalOrExpr] ... ||= ...
# 101| getDesugared: [StmtSequence] ...
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getRightOperand: [ConstantReadAccess] Foo
# 101| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 101| getStmt: [AssignExpr] ... = ...
# 101| getAnOperand/getLeftOperand: [ConstantAssignment] MemberConstant
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
# 101| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 101| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] MemberConstant
# 101| getScopeExpr: [LocalVariableAccess] __synth__0
# 101| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 8
# 102| [AssignLogicalOrExpr] ... ||= ...
# 102| getDesugared: [StmtSequence] ...
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getRightOperand: [MethodCall] call to bar
# 102| getReceiver: [MethodCall] call to foo
# 102| getReceiver: [SelfVariableAccess] self
# 102| getArgument: [IntegerLiteral] 1
# 102| getAnOperand/getLeftOperand: [LocalVariableAccess] __synth__0
# 102| getStmt: [AssignExpr] ... = ...
# 102| getAnOperand/getLeftOperand: [ConstantAssignment] OtherConstant
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
# 102| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 102| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] OtherConstant
# 102| getScopeExpr: [LocalVariableAccess] __synth__0
# 102| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
# 103| [AssignLogicalOrExpr] ... ||= ...
# 103| getDesugared: [AssignExpr] ... = ...
# 103| getAnOperand/getLeftOperand: [ConstantAssignment, ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getRightOperand: [LogicalOrExpr] ... || ...
# 103| getAnOperand/getLeftOperand/getReceiver: [ConstantReadAccess] CONSTANT4
# 103| getAnOperand/getArgument/getRightOperand: [IntegerLiteral] 7
params/params.rb: params/params.rb:
# 8| [HashLiteral] {...} # 8| [HashLiteral] {...}
# 8| getDesugared: [MethodCall] call to [] # 8| getDesugared: [MethodCall] call to []

View File

@@ -5537,6 +5537,46 @@ operations/operations.rb:
# 96| 0: [GlobalVariable] $global_var # 96| 0: [GlobalVariable] $global_var
# 96| 1: [ReservedWord] *= # 96| 1: [ReservedWord] *=
# 96| 2: [Integer] 6 # 96| 2: [Integer] 6
# 98| 66: [Assignment] Assignment
# 98| 0: [Constant] CONSTANT1
# 98| 1: [ReservedWord] =
# 98| 2: [Integer] 5
# 99| 67: [OperatorAssignment] OperatorAssignment
# 99| 0: [Constant] CONSTANT2
# 99| 1: [ReservedWord] +=
# 99| 2: [Integer] 6
# 100| 68: [OperatorAssignment] OperatorAssignment
# 100| 0: [Constant] CONSTANT3
# 100| 1: [ReservedWord] ||=
# 100| 2: [Integer] 7
# 101| 69: [OperatorAssignment] OperatorAssignment
# 101| 0: [ScopeResolution] ScopeResolution
# 101| 0: [Constant] Foo
# 101| 1: [ReservedWord] ::
# 101| 2: [Constant] MemberConstant
# 101| 1: [ReservedWord] ||=
# 101| 2: [Integer] 8
# 102| 70: [OperatorAssignment] OperatorAssignment
# 102| 0: [ScopeResolution] ScopeResolution
# 102| 0: [Call] Call
# 102| 0: [Call] Call
# 102| 0: [Identifier] foo
# 102| 1: [ArgumentList] ArgumentList
# 102| 0: [ReservedWord] (
# 102| 1: [Integer] 1
# 102| 2: [ReservedWord] )
# 102| 1: [ReservedWord] .
# 102| 2: [Identifier] bar
# 102| 1: [ReservedWord] ::
# 102| 2: [Constant] OtherConstant
# 102| 1: [ReservedWord] ||=
# 102| 2: [Integer] 7
# 103| 71: [OperatorAssignment] OperatorAssignment
# 103| 0: [ScopeResolution] ScopeResolution
# 103| 0: [ReservedWord] ::
# 103| 1: [Constant] CONSTANT4
# 103| 1: [ReservedWord] ||=
# 103| 2: [Integer] 7
# 1| [Comment] # Start with assignments to all the identifiers used below, so that they are # 1| [Comment] # Start with assignments to all the identifiers used below, so that they are
# 2| [Comment] # interpreted as variables. # 2| [Comment] # interpreted as variables.
# 22| [Comment] # Unary operations # 22| [Comment] # Unary operations

View File

@@ -888,6 +888,13 @@ exprValue
| operations/operations.rb:92:10:92:10 | 4 | 4 | int | | operations/operations.rb:92:10:92:10 | 4 | 4 | int |
| operations/operations.rb:95:15:95:15 | 5 | 5 | int | | operations/operations.rb:95:15:95:15 | 5 | 5 | int |
| operations/operations.rb:96:16:96:16 | 6 | 6 | int | | operations/operations.rb:96:16:96:16 | 6 | 6 | int |
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
| params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int |
| params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:19:47:21 | :bar | :bar | symbol |
| params/params.rb:47:24:47:24 | 2 | 2 | int | | params/params.rb:47:24:47:24 | 2 | 2 | int |
@@ -1764,6 +1771,13 @@ exprCfgNodeValue
| operations/operations.rb:92:10:92:10 | 4 | 4 | int | | operations/operations.rb:92:10:92:10 | 4 | 4 | int |
| operations/operations.rb:95:15:95:15 | 5 | 5 | int | | operations/operations.rb:95:15:95:15 | 5 | 5 | int |
| operations/operations.rb:96:16:96:16 | 6 | 6 | int | | operations/operations.rb:96:16:96:16 | 6 | 6 | int |
| operations/operations.rb:98:13:98:13 | 5 | 5 | int |
| operations/operations.rb:99:14:99:14 | 6 | 6 | int |
| operations/operations.rb:100:15:100:15 | 7 | 7 | int |
| operations/operations.rb:101:25:101:25 | 8 | 8 | int |
| operations/operations.rb:102:5:102:5 | 1 | 1 | int |
| operations/operations.rb:102:31:102:31 | 7 | 7 | int |
| operations/operations.rb:103:17:103:17 | 7 | 7 | int |
| params/params.rb:41:46:41:46 | 7 | 7 | int | | params/params.rb:41:46:41:46 | 7 | 7 | int |
| params/params.rb:47:19:47:21 | :bar | :bar | symbol | | params/params.rb:47:19:47:21 | :bar | :bar | symbol |
| params/params.rb:47:24:47:24 | 2 | 2 | int | | params/params.rb:47:24:47:24 | 2 | 2 | int |

View File

@@ -52,6 +52,19 @@ assignments
| operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr | | operations.rb:95:1:95:15 | ... = ... | = | operations.rb:95:1:95:11 | $global_var | operations.rb:95:15:95:15 | 5 | AssignExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr | | operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:1:96:11 | $global_var | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | operations.rb:98:13:98:13 | 5 | AssignExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | operations.rb:101:1:101:3 | Foo | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | operations.rb:102:1:102:10 | call to bar | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignOperations assignOperations
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
@@ -69,6 +82,11 @@ assignOperations
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | | operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | | operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignArithmeticOperations assignArithmeticOperations
| operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr | | operations.rb:69:1:69:8 | ... += ... | += | operations.rb:69:1:69:1 | x | operations.rb:69:6:69:8 | 128 | AssignAddExpr |
| operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr | | operations.rb:70:1:70:7 | ... -= ... | -= | operations.rb:70:1:70:1 | y | operations.rb:70:6:70:7 | 32 | AssignSubExpr |
@@ -79,9 +97,14 @@ assignArithmeticOperations
| operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr | | operations.rb:89:3:89:9 | ... += ... | += | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AssignAddExpr |
| operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr | | operations.rb:92:3:92:10 | ... /= ... | /= | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | AssignDivExpr |
| operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr | | operations.rb:96:1:96:16 | ... *= ... | *= | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | AssignMulExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
assignLogicalOperations assignLogicalOperations
| operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr | | operations.rb:77:2:77:8 | ... &&= ... | &&= | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | AssignLogicalAndExpr |
| operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr | | operations.rb:78:2:78:8 | ... \|\|= ... | \|\|= | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | AssignLogicalOrExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
assignBitwiseOperations assignBitwiseOperations
| operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr | | operations.rb:81:2:81:8 | ... <<= ... | <<= | operations.rb:81:2:81:2 | x | operations.rb:81:8:81:8 | 2 | AssignLShiftExpr |
| operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr | | operations.rb:82:2:82:8 | ... >>= ... | >>= | operations.rb:82:2:82:2 | y | operations.rb:82:8:82:8 | 3 | AssignRShiftExpr |

View File

@@ -40,6 +40,11 @@ binaryOperations
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | | operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | | operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
binaryArithmeticOperations binaryArithmeticOperations
| operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr | | operations.rb:32:1:32:7 | ... + ... | + | operations.rb:32:1:32:1 | w | operations.rb:32:5:32:7 | 234 | AddExpr |
| operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr | | operations.rb:33:1:33:6 | ... - ... | - | operations.rb:33:1:33:1 | x | operations.rb:33:5:33:6 | 17 | SubExpr |
@@ -56,6 +61,7 @@ binaryArithmeticOperations
| operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr | | operations.rb:89:6:89:7 | ... + ... | + | operations.rb:89:3:89:4 | @x | operations.rb:89:9:89:9 | 2 | AddExpr |
| operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr | | operations.rb:92:7:92:8 | ... / ... | / | operations.rb:92:3:92:5 | @@y | operations.rb:92:10:92:10 | 4 | DivExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | operations.rb:99:14:99:14 | 6 | AddExpr |
binaryLogicalOperations binaryLogicalOperations
| operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr | | operations.rb:40:1:40:10 | ... && ... | && | operations.rb:40:1:40:3 | foo | operations.rb:40:8:40:10 | bar | LogicalAndExpr |
| operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr | | operations.rb:41:1:41:11 | ... and ... | and | operations.rb:41:1:41:3 | baz | operations.rb:41:9:41:11 | qux | LogicalAndExpr |
@@ -63,6 +69,10 @@ binaryLogicalOperations
| operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr | | operations.rb:43:1:43:6 | ... \|\| ... | \|\| | operations.rb:43:1:43:1 | x | operations.rb:43:6:43:6 | y | LogicalOrExpr |
| operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr | | operations.rb:77:4:77:6 | ... && ... | && | operations.rb:77:2:77:2 | x | operations.rb:77:8:77:8 | y | LogicalAndExpr |
| operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr | | operations.rb:78:4:78:6 | ... \|\| ... | \|\| | operations.rb:78:2:78:2 | a | operations.rb:78:8:78:8 | b | LogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |
binaryBitwiseOperations binaryBitwiseOperations
| operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr | | operations.rb:46:1:46:6 | ... << ... | << | operations.rb:46:1:46:1 | x | operations.rb:46:6:46:6 | 3 | LShiftExpr |
| operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr | | operations.rb:47:1:47:7 | ... >> ... | >> | operations.rb:47:1:47:1 | y | operations.rb:47:6:47:7 | 16 | RShiftExpr |

View File

@@ -194,3 +194,39 @@
| operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr | | operations.rb:96:1:96:16 | ... = ... | = | operations.rb:96:13:96:14 | ... * ... | AssignExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:1:96:11 | $global_var | MulExpr |
| operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr | | operations.rb:96:13:96:14 | ... * ... | * | operations.rb:96:16:96:16 | 6 | MulExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:1:98:9 | CONSTANT1 | AssignExpr |
| operations.rb:98:1:98:13 | ... = ... | = | operations.rb:98:13:98:13 | 5 | AssignExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:1:99:9 | CONSTANT2 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... += ... | += | operations.rb:99:14:99:14 | 6 | AssignAddExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:1:99:9 | CONSTANT2 | AssignExpr |
| operations.rb:99:1:99:14 | ... = ... | = | operations.rb:99:11:99:12 | ... + ... | AssignExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:1:99:9 | CONSTANT2 | AddExpr |
| operations.rb:99:11:99:12 | ... + ... | + | operations.rb:99:14:99:14 | 6 | AddExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:1:100:9 | CONSTANT3 | AssignExpr |
| operations.rb:100:1:100:15 | ... = ... | = | operations.rb:100:11:100:13 | ... \|\| ... | AssignExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:1:100:9 | CONSTANT3 | AssignLogicalOrExpr |
| operations.rb:100:1:100:15 | ... \|\|= ... | \|\|= | operations.rb:100:15:100:15 | 7 | AssignLogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:1:100:9 | CONSTANT3 | LogicalOrExpr |
| operations.rb:100:11:100:13 | ... \|\| ... | \|\| | operations.rb:100:15:100:15 | 7 | LogicalOrExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | Foo | AssignExpr |
| operations.rb:101:1:101:3 | ... = ... | = | operations.rb:101:1:101:3 | __synth__0 | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:1:101:19 | MemberConstant | AssignExpr |
| operations.rb:101:1:101:25 | ... = ... | = | operations.rb:101:21:101:23 | ... \|\| ... | AssignExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:1:101:19 | MemberConstant | AssignLogicalOrExpr |
| operations.rb:101:1:101:25 | ... \|\|= ... | \|\|= | operations.rb:101:25:101:25 | 8 | AssignLogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:1:101:19 | MemberConstant | LogicalOrExpr |
| operations.rb:101:21:101:23 | ... \|\| ... | \|\| | operations.rb:101:25:101:25 | 8 | LogicalOrExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | __synth__0 | AssignExpr |
| operations.rb:102:1:102:10 | ... = ... | = | operations.rb:102:1:102:10 | call to bar | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:1:102:25 | OtherConstant | AssignExpr |
| operations.rb:102:1:102:31 | ... = ... | = | operations.rb:102:27:102:29 | ... \|\| ... | AssignExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:1:102:25 | OtherConstant | AssignLogicalOrExpr |
| operations.rb:102:1:102:31 | ... \|\|= ... | \|\|= | operations.rb:102:31:102:31 | 7 | AssignLogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:1:102:25 | OtherConstant | LogicalOrExpr |
| operations.rb:102:27:102:29 | ... \|\| ... | \|\| | operations.rb:102:31:102:31 | 7 | LogicalOrExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:1:103:11 | CONSTANT4 | AssignExpr |
| operations.rb:103:1:103:17 | ... = ... | = | operations.rb:103:13:103:15 | ... \|\| ... | AssignExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:1:103:11 | CONSTANT4 | AssignLogicalOrExpr |
| operations.rb:103:1:103:17 | ... \|\|= ... | \|\|= | operations.rb:103:17:103:17 | 7 | AssignLogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:1:103:11 | CONSTANT4 | LogicalOrExpr |
| operations.rb:103:13:103:15 | ... \|\| ... | \|\| | operations.rb:103:17:103:17 | 7 | LogicalOrExpr |

View File

@@ -94,3 +94,10 @@ end
$global_var = 5 $global_var = 5
$global_var *= 6 $global_var *= 6
CONSTANT1 = 5
CONSTANT2 += 6
CONSTANT3 ||= 7
Foo::MemberConstant ||= 8
foo(1).bar::OtherConstant ||= 7
::CONSTANT4 ||= 7

View File

@@ -3762,6 +3762,81 @@ cfg.rb:
# 215| self # 215| self
#-----| -> call to something_else #-----| -> call to something_else
constant_compound_assign.rb:
# 1| Foo
#-----| -> foo_before
# 1| enter constant_compound_assign.rb
#-----| -> Foo
# 1| exit constant_compound_assign.rb
# 1| exit constant_compound_assign.rb (normal)
#-----| -> exit constant_compound_assign.rb
# 2| foo_before
#-----| -> FOO_CONSTANT
# 5| FOO_CONSTANT
#-----| -> FOO_CONSTANT
# 5| FOO_CONSTANT
#-----| true -> ... || ...
#-----| false -> 123
# 5| ... = ...
#-----| -> foo_after
# 5| ... || ...
#-----| -> ... = ...
# 5| 123
#-----| -> ... || ...
# 7| foo_after
#-----| -> top_before
# 11| top_before
#-----| -> TOP_CONSTANT
# 14| TOP_CONSTANT
#-----| -> TOP_CONSTANT
# 14| TOP_CONSTANT
#-----| true -> ... || ...
#-----| false -> 123
# 14| ... = ...
#-----| -> top_after
# 14| ... || ...
#-----| -> ... = ...
# 14| 123
#-----| -> ... || ...
# 16| top_after
#-----| -> TOP_CONSTANT2
# 19| TOP_CONSTANT2
#-----| -> TOP_CONSTANT2
# 19| TOP_CONSTANT2
#-----| true -> ... || ...
#-----| false -> 123
# 19| ... = ...
#-----| -> top_after2
# 19| ... || ...
#-----| -> ... = ...
# 19| 123
#-----| -> ... || ...
# 21| top_after2
#-----| -> exit constant_compound_assign.rb (normal)
desugar.rb: desugar.rb:
# 1| enter m1 # 1| enter m1
#-----| -> x #-----| -> x

View File

@@ -212,6 +212,9 @@ positionalArguments
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:10:205:10 | 1 | | cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:10:205:10 | 1 |
| cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:12:205:12 | 2 | | cfg.rb:205:1:205:23 | call to bar | cfg.rb:205:12:205:12 | 2 |
| cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 | | cfg.rb:205:4:205:5 | call to == | cfg.rb:205:1:205:3 | __synth__0__1 |
| constant_compound_assign.rb:5:18:5:20 | ... \|\| ... | constant_compound_assign.rb:5:22:5:24 | 123 |
| constant_compound_assign.rb:14:14:14:16 | ... \|\| ... | constant_compound_assign.rb:14:18:14:20 | 123 |
| constant_compound_assign.rb:19:17:19:19 | ... \|\| ... | constant_compound_assign.rb:19:21:19:23 | 123 |
| desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 | | desugar.rb:2:5:2:6 | ... + ... | desugar.rb:2:8:2:8 | 1 |
| desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... | | desugar.rb:6:3:6:13 | call to count= | desugar.rb:6:17:6:17 | ... = ... |
| desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 | | desugar.rb:10:3:10:10 | call to []= | desugar.rb:10:9:10:9 | 0 |

View File

@@ -0,0 +1,22 @@
module Foo
def foo_before
end
FOO_CONSTANT ||= 123
def foo_after
end
end
def top_before
end
TOP_CONSTANT ||= 123
def top_after
end
::TOP_CONSTANT2 ||= 123
def top_after2
end

View File

@@ -3,7 +3,6 @@
| array_flow.rb:180:28:180:46 | # $ hasValueFlow=19 | Missing result:hasValueFlow=19 | | 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:226:10:226:13 | ...[...] | Unexpected result: hasValueFlow=25 |
| array_flow.rb:242:14:242:14 | x | Unexpected result: hasValueFlow=27.2 | | 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:319:10:319:13 | ...[...] | Unexpected result: hasValueFlow=36.1 |
| array_flow.rb:320:10:320: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 | | 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: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.2 |
| array_flow.rb:494:10:494:13 | ...[...] | Unexpected result: hasValueFlow=54.3 | | 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: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:580:19:580:37 | # $ hasValueFlow=64 | Missing result:hasValueFlow=64 |
| array_flow.rb:582:16:582:34 | # $ 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 failures
edges 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: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] : | | 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 |
| 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 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: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] : | | 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 |
| 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 #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: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: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 : | | instance_variables.rb:29:6:29:18 | call to inc_field | instance_variables.rb:28:15:28:22 | call to taint : | instance_variables.rb:29:6:29:18 | call to inc_field | $@ | instance_variables.rb:28:15:28:22 | call to taint : | call to taint : |

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