From 5d1c399371df244cd5af7f726a4b3778606ff297 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Fri, 14 Oct 2022 13:12:41 +0200 Subject: [PATCH 1/5] Ruby: Add more data-flow tests for captured variables --- .../dataflow/global/Flow.expected | 192 ++++++++++++++++-- .../global/TypeTrackingInlineTest.expected | 13 +- .../dataflow/global/captured_variables.rb | 166 ++++++++++++++- 3 files changed, 345 insertions(+), 26 deletions(-) diff --git a/ruby/ql/test/library-tests/dataflow/global/Flow.expected b/ruby/ql/test/library-tests/dataflow/global/Flow.expected index 85daf1fbf610..523259015973 100644 --- a/ruby/ql/test/library-tests/dataflow/global/Flow.expected +++ b/ruby/ql/test/library-tests/dataflow/global/Flow.expected @@ -1,11 +1,52 @@ testFailures edges -| 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: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: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:9:24:9:24 | x | captured_variables.rb:10:20:10:20 | x | +| captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:9:24:9:24 | x | +| captured_variables.rb:29:33:29:33 | x | captured_variables.rb:31:14:31:14 | x | +| captured_variables.rb:35:29:35:38 | call to taint | captured_variables.rb:29:33:29:33 | x | +| captured_variables.rb:40:31:40:31 | x | captured_variables.rb:42:14:42:14 | x | +| captured_variables.rb:46:27:46:36 | call to taint | captured_variables.rb:40:31:40:31 | x | +| captured_variables.rb:48:1:48:1 | x | captured_variables.rb:50:10:50:10 | x | +| captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:48:1:48:1 | x | +| captured_variables.rb:51:5:51:5 | x | captured_variables.rb:54:6:54:6 | x | +| captured_variables.rb:51:9:51:16 | call to taint | captured_variables.rb:51:5:51:5 | x | +| captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:18:58:18 | x | +| captured_variables.rb:58:18:58:18 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | +| captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:16:61:21 | self [@field] | +| captured_variables.rb:61:16:61:21 | @field | captured_variables.rb:61:9:61:21 | return | +| captured_variables.rb:61:16:61:21 | self [@field] | captured_variables.rb:61:16:61:21 | @field | +| captured_variables.rb:66:1:66:3 | [post] foo [@field] | captured_variables.rb:72:6:72:8 | foo [@field] | +| captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:57:19:57:19 | x | +| captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:66:1:66:3 | [post] foo [@field] | +| captured_variables.rb:66:15:66:22 | call to taint | instance_variables.rb:10:19:10:19 | x | +| captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | +| captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:72:6:72:18 | call to get_field | +| captured_variables.rb:72:6:72:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | +| captured_variables.rb:85:1:85:1 | y | captured_variables.rb:87:10:87:10 | y | +| captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:85:1:85:1 | y | +| captured_variables.rb:88:5:88:5 | y | captured_variables.rb:87:10:87:10 | y | +| captured_variables.rb:88:5:88:5 | y | captured_variables.rb:91:6:91:6 | y | +| captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:88:5:88:5 | y | +| captured_variables.rb:100:21:100:21 | x | captured_variables.rb:101:11:101:11 | x | +| captured_variables.rb:101:11:101:11 | x | captured_variables.rb:104:31:104:31 | x | +| captured_variables.rb:104:17:104:24 | call to taint | captured_variables.rb:100:21:100:21 | x | +| captured_variables.rb:104:31:104:31 | x | captured_variables.rb:105:10:105:10 | x | +| captured_variables.rb:109:5:109:5 | x | captured_variables.rb:112:18:112:18 | x | +| captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:109:5:109:5 | x | +| captured_variables.rb:113:13:113:13 | x | captured_variables.rb:112:18:112:18 | x | +| captured_variables.rb:113:13:113:13 | x | captured_variables.rb:118:10:118:10 | x | +| captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:113:13:113:13 | x | +| captured_variables.rb:160:9:160:10 | [post] self [@x] | captured_variables.rb:174:1:174:24 | call to new [@x] | +| captured_variables.rb:160:14:160:22 | call to taint | captured_variables.rb:160:9:160:10 | [post] self [@x] | +| captured_variables.rb:167:5:171:7 | self in baz [@x] | captured_variables.rb:169:18:169:19 | self [@x] | +| captured_variables.rb:169:18:169:19 | self [@x] | captured_variables.rb:169:18:169:19 | @x | +| captured_variables.rb:174:1:174:24 | call to new [@x] | captured_variables.rb:167:5:171:7 | self in baz [@x] | +| captured_variables.rb:178:9:178:10 | [post] self [@x] | captured_variables.rb:193:1:193:1 | [post] c [@x] | +| captured_variables.rb:178:14:178:22 | call to taint | captured_variables.rb:178:9:178:10 | [post] self [@x] | +| captured_variables.rb:185:5:189:7 | self in baz [@x] | captured_variables.rb:187:18:187:19 | self [@x] | +| captured_variables.rb:187:18:187:19 | self [@x] | captured_variables.rb:187:18:187:19 | @x | +| captured_variables.rb:193:1:193:1 | [post] c [@x] | captured_variables.rb:194:1:194:1 | c [@x] | +| captured_variables.rb:194:1:194:1 | c [@x] | captured_variables.rb:185:5:189:7 | self in baz [@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:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:16:14:21 | self [@field] | @@ -28,10 +69,12 @@ edges | instance_variables.rb:32:13:32:21 | call to taint | instance_variables.rb:48:20:48:20 | x | | instance_variables.rb:33:13:33:13 | x | instance_variables.rb:22:20:22:24 | field | | instance_variables.rb:33:13:33:13 | x | instance_variables.rb:33:9:33:14 | call to new [@field] | +| instance_variables.rb:36:10:36:23 | call to new [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:36:10:36:23 | call to new [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:36:10:36:23 | call to new [@field] | instance_variables.rb:36:10:36:33 | call to get_field | | instance_variables.rb:36:14:36:22 | call to taint | instance_variables.rb:22:20:22:24 | field | | instance_variables.rb:36:14:36:22 | call to taint | instance_variables.rb:36:10:36:23 | call to new [@field] | +| instance_variables.rb:39:6:39:23 | call to bar [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:39:6:39:23 | call to bar [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:39:6:39:23 | call to bar [@field] | instance_variables.rb:39:6:39:33 | call to get_field | | instance_variables.rb:39:14:39:22 | call to taint | instance_variables.rb:31:18:31:18 | x | @@ -39,11 +82,14 @@ edges | instance_variables.rb:43:9:43:17 | call to taint | instance_variables.rb:121:7:121:24 | call to new | | instance_variables.rb:48:20:48:20 | x | instance_variables.rb:49:14:49:14 | x | | instance_variables.rb:54:1:54:3 | [post] foo [@field] | instance_variables.rb:55:6:55:8 | foo [@field] | +| instance_variables.rb:54:15:54:23 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:54:15:54:23 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:54:15:54:23 | call to taint | instance_variables.rb:54:1:54:3 | [post] foo [@field] | +| instance_variables.rb:55:6:55:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:55:6:55:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:55:6:55:8 | foo [@field] | instance_variables.rb:55:6:55:18 | call to get_field | | instance_variables.rb:58:1:58:3 | [post] bar [@field] | instance_variables.rb:59:6:59:8 | bar [@field] | +| instance_variables.rb:58:15:58:22 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:58:15:58:22 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:58:15:58:22 | call to taint | instance_variables.rb:58:1:58:3 | [post] bar [@field] | | instance_variables.rb:59:6:59:8 | bar [@field] | instance_variables.rb:16:5:18:7 | self in inc_field [@field] | @@ -53,83 +99,150 @@ edges | instance_variables.rb:63:6:63:9 | foo1 [@field] | instance_variables.rb:63:6:63:15 | call to field | | instance_variables.rb:66:1:66:4 | [post] foo2 [@field] | instance_variables.rb:67:6:67:9 | foo2 [@field] | | instance_variables.rb:66:14:66:22 | call to taint | instance_variables.rb:66:1:66:4 | [post] foo2 [@field] | +| instance_variables.rb:67:6:67:9 | foo2 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:67:6:67:9 | foo2 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:67:6:67:9 | foo2 [@field] | instance_variables.rb:67:6:67:19 | call to get_field | | instance_variables.rb:70:1:70:4 | [post] foo3 [@field] | instance_variables.rb:71:6:71:9 | foo3 [@field] | | instance_variables.rb:70:1:70:4 | [post] foo3 [@field] | instance_variables.rb:83:6:83:9 | foo3 [@field] | +| instance_variables.rb:70:16:70:24 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:70:16:70:24 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:70:16:70:24 | call to taint | instance_variables.rb:70:1:70:4 | [post] foo3 [@field] | | instance_variables.rb:71:6:71:9 | foo3 [@field] | instance_variables.rb:71:6:71:15 | call to field | | instance_variables.rb:78:2:78:5 | [post] foo5 [@field] | instance_variables.rb:79:6:79:9 | foo5 [@field] | | instance_variables.rb:78:2:78:5 | [post] foo5 [@field] | instance_variables.rb:84:6:84:9 | foo5 [@field] | +| instance_variables.rb:78:18:78:26 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:78:18:78:26 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:78:18:78:26 | call to taint | instance_variables.rb:78:2:78:5 | [post] foo5 [@field] | +| instance_variables.rb:79:6:79:9 | foo5 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:79:6:79:9 | foo5 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:79:6:79:9 | foo5 [@field] | instance_variables.rb:79:6:79:19 | call to get_field | | instance_variables.rb:82:15:82:18 | [post] foo6 [@field] | instance_variables.rb:85:6:85:9 | foo6 [@field] | +| instance_variables.rb:82:32:82:40 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:82:32:82:40 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:82:32:82:40 | call to taint | instance_variables.rb:82:15:82:18 | [post] foo6 [@field] | +| instance_variables.rb:83:6:83:9 | foo3 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:83:6:83:9 | foo3 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:83:6:83:9 | foo3 [@field] | instance_variables.rb:83:6:83:19 | call to get_field | +| instance_variables.rb:84:6:84:9 | foo5 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:84:6:84:9 | foo5 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:84:6:84:9 | foo5 [@field] | instance_variables.rb:84:6:84:19 | call to get_field | +| instance_variables.rb:85:6:85:9 | foo6 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:85:6:85:9 | foo6 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:85:6:85:9 | foo6 [@field] | instance_variables.rb:85:6:85:19 | call to get_field | | instance_variables.rb:89:15:89:18 | [post] foo7 [@field] | instance_variables.rb:90:6:90:9 | foo7 [@field] | | instance_variables.rb:89:25:89:28 | [post] foo8 [@field] | instance_variables.rb:91:6:91:9 | foo8 [@field] | +| instance_variables.rb:89:45:89:53 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:89:45:89:53 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:89:45:89:53 | call to taint | instance_variables.rb:89:15:89:18 | [post] foo7 [@field] | | instance_variables.rb:89:45:89:53 | call to taint | instance_variables.rb:89:25:89:28 | [post] foo8 [@field] | +| instance_variables.rb:90:6:90:9 | foo7 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:90:6:90:9 | foo7 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:90:6:90:9 | foo7 [@field] | instance_variables.rb:90:6:90:19 | call to get_field | +| instance_variables.rb:91:6:91:9 | foo8 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:91:6:91:9 | foo8 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:91:6:91:9 | foo8 [@field] | instance_variables.rb:91:6:91:19 | call to get_field | | instance_variables.rb:95:22:95:25 | [post] foo9 [@field] | instance_variables.rb:96:6:96:9 | foo9 [@field] | | instance_variables.rb:95:32:95:36 | [post] foo10 [@field] | instance_variables.rb:97:6:97:10 | foo10 [@field] | +| instance_variables.rb:95:53:95:61 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:95:53:95:61 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:95:53:95:61 | call to taint | instance_variables.rb:95:22:95:25 | [post] foo9 [@field] | | instance_variables.rb:95:53:95:61 | call to taint | instance_variables.rb:95:32:95:36 | [post] foo10 [@field] | +| instance_variables.rb:96:6:96:9 | foo9 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:96:6:96:9 | foo9 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:96:6:96:9 | foo9 [@field] | instance_variables.rb:96:6:96:19 | call to get_field | +| instance_variables.rb:97:6:97:10 | foo10 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:97:6:97:10 | foo10 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:97:6:97:10 | foo10 [@field] | instance_variables.rb:97:6:97:20 | call to get_field | | instance_variables.rb:100:5:100:5 | [post] x [@field] | instance_variables.rb:104:14:104:18 | [post] foo11 [@field] | | instance_variables.rb:100:5:100:5 | [post] x [@field] | instance_variables.rb:108:15:108:19 | [post] foo12 [@field] | | instance_variables.rb:100:5:100:5 | [post] x [@field] | instance_variables.rb:113:22:113:26 | [post] foo13 [@field] | +| instance_variables.rb:100:17:100:25 | call to taint | captured_variables.rb:57:19:57:19 | x | | instance_variables.rb:100:17:100:25 | call to taint | instance_variables.rb:10:19:10:19 | x | | instance_variables.rb:100:17:100:25 | call to taint | instance_variables.rb:100:5:100:5 | [post] x [@field] | | instance_variables.rb:104:14:104:18 | [post] foo11 [@field] | instance_variables.rb:105:6:105:10 | foo11 [@field] | +| instance_variables.rb:105:6:105:10 | foo11 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:105:6:105:10 | foo11 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:105:6:105:10 | foo11 [@field] | instance_variables.rb:105:6:105:20 | call to get_field | | instance_variables.rb:108:15:108:19 | [post] foo12 [@field] | instance_variables.rb:109:6:109:10 | foo12 [@field] | +| instance_variables.rb:109:6:109:10 | foo12 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:109:6:109:10 | foo12 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:109:6:109:10 | foo12 [@field] | instance_variables.rb:109:6:109:20 | call to get_field | | instance_variables.rb:113:22:113:26 | [post] foo13 [@field] | instance_variables.rb:114:6:114:10 | foo13 [@field] | +| instance_variables.rb:114:6:114:10 | foo13 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:114:6:114:10 | foo13 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:114:6:114:10 | foo13 [@field] | instance_variables.rb:114:6:114:20 | call to get_field | | instance_variables.rb:116:1:116:5 | foo15 [@field] | instance_variables.rb:117:6:117:10 | foo15 [@field] | | instance_variables.rb:116:9:116:26 | call to new [@field] | instance_variables.rb:116:1:116:5 | foo15 [@field] | | instance_variables.rb:116:17:116:25 | call to taint | instance_variables.rb:22:20:22:24 | field | | instance_variables.rb:116:17:116:25 | call to taint | instance_variables.rb:116:9:116:26 | call to new [@field] | +| instance_variables.rb:117:6:117:10 | foo15 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:117:6:117:10 | foo15 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:117:6:117:10 | foo15 [@field] | instance_variables.rb:117:6:117:20 | call to get_field | | instance_variables.rb:119:6:119:10 | [post] foo16 [@field] | instance_variables.rb:120:6:120:10 | foo16 [@field] | | instance_variables.rb:119:28:119:36 | call to taint | instance_variables.rb:27:25:27:29 | field | | instance_variables.rb:119:28:119:36 | call to taint | instance_variables.rb:119:6:119:10 | [post] foo16 [@field] | +| instance_variables.rb:120:6:120:10 | foo16 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | instance_variables.rb:120:6:120:10 | foo16 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | | instance_variables.rb:120:6:120:10 | foo16 [@field] | instance_variables.rb:120:6:120:20 | call to get_field | | instance_variables.rb:121:1:121:3 | bar | instance_variables.rb:122:6:122:8 | bar | | instance_variables.rb:121:7:121:24 | call to new | instance_variables.rb:121:1:121:3 | bar | nodes -| 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: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: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:32:31:32:31 | 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:9:24:9:24 | x | semmle.label | x | +| captured_variables.rb:10:20:10:20 | x | semmle.label | x | +| captured_variables.rb:13:20:13:29 | call to taint | semmle.label | call to taint | +| captured_variables.rb:29:33:29:33 | x | semmle.label | x | +| captured_variables.rb:31:14:31:14 | x | semmle.label | x | +| captured_variables.rb:35:29:35:38 | call to taint | semmle.label | call to taint | +| captured_variables.rb:40:31:40:31 | x | semmle.label | x | +| captured_variables.rb:42:14:42:14 | x | semmle.label | x | +| captured_variables.rb:46:27:46:36 | call to taint | semmle.label | call to taint | +| captured_variables.rb:48:1:48:1 | x | semmle.label | x | +| captured_variables.rb:48:5:48:12 | call to taint | semmle.label | call to taint | +| captured_variables.rb:50:10:50:10 | x | semmle.label | x | +| captured_variables.rb:51:5:51:5 | x | semmle.label | x | +| captured_variables.rb:51:9:51:16 | call to taint | semmle.label | call to taint | +| captured_variables.rb:54:6:54:6 | x | semmle.label | x | +| captured_variables.rb:57:19:57:19 | x | semmle.label | x | +| captured_variables.rb:58:9:58:14 | [post] self [@field] | semmle.label | [post] self [@field] | +| captured_variables.rb:58:18:58:18 | x | semmle.label | x | +| captured_variables.rb:60:5:62:7 | self in get_field [@field] | semmle.label | self in get_field [@field] | +| captured_variables.rb:61:9:61:21 | return | semmle.label | return | +| captured_variables.rb:61:16:61:21 | @field | semmle.label | @field | +| captured_variables.rb:61:16:61:21 | self [@field] | semmle.label | self [@field] | +| captured_variables.rb:66:1:66:3 | [post] foo [@field] | semmle.label | [post] foo [@field] | +| captured_variables.rb:66:15:66:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:72:6:72:8 | foo [@field] | semmle.label | foo [@field] | +| captured_variables.rb:72:6:72:18 | call to get_field | semmle.label | call to get_field | +| captured_variables.rb:85:1:85:1 | y | semmle.label | y | +| captured_variables.rb:85:5:85:12 | call to taint | semmle.label | call to taint | +| captured_variables.rb:87:10:87:10 | y | semmle.label | y | +| captured_variables.rb:88:5:88:5 | y | semmle.label | y | +| captured_variables.rb:88:9:88:16 | call to taint | semmle.label | call to taint | +| captured_variables.rb:91:6:91:6 | y | semmle.label | y | +| captured_variables.rb:100:21:100:21 | x | semmle.label | x | +| captured_variables.rb:101:11:101:11 | x | semmle.label | x | +| captured_variables.rb:104:17:104:24 | call to taint | semmle.label | call to taint | +| captured_variables.rb:104:31:104:31 | x | semmle.label | x | +| captured_variables.rb:105:10:105:10 | x | semmle.label | x | +| captured_variables.rb:109:5:109:5 | x | semmle.label | x | +| captured_variables.rb:109:9:109:17 | call to taint | semmle.label | call to taint | +| captured_variables.rb:112:18:112:18 | x | semmle.label | x | +| captured_variables.rb:113:13:113:13 | x | semmle.label | x | +| captured_variables.rb:113:17:113:25 | call to taint | semmle.label | call to taint | +| captured_variables.rb:118:10:118:10 | x | semmle.label | x | +| captured_variables.rb:160:9:160:10 | [post] self [@x] | semmle.label | [post] self [@x] | +| captured_variables.rb:160:14:160:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:167:5:171:7 | self in baz [@x] | semmle.label | self in baz [@x] | +| captured_variables.rb:169:18:169:19 | @x | semmle.label | @x | +| captured_variables.rb:169:18:169:19 | self [@x] | semmle.label | self [@x] | +| captured_variables.rb:174:1:174:24 | call to new [@x] | semmle.label | call to new [@x] | +| captured_variables.rb:178:9:178:10 | [post] self [@x] | semmle.label | [post] self [@x] | +| captured_variables.rb:178:14:178:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:185:5:189:7 | self in baz [@x] | semmle.label | self in baz [@x] | +| captured_variables.rb:187:18:187:19 | @x | semmle.label | @x | +| captured_variables.rb:187:18:187:19 | self [@x] | semmle.label | self [@x] | +| captured_variables.rb:193:1:193:1 | [post] c [@x] | semmle.label | [post] c [@x] | +| captured_variables.rb:194:1:194:1 | c [@x] | semmle.label | c [@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:18:11:18 | x | semmle.label | x | @@ -235,45 +348,88 @@ nodes | instance_variables.rb:121:7:121:24 | call to new | semmle.label | call to new | | instance_variables.rb:122:6:122:8 | bar | semmle.label | bar | subpaths +| captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | captured_variables.rb:66:1:66:3 | [post] foo [@field] | +| captured_variables.rb:66:15:66:22 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | captured_variables.rb:66:1:66:3 | [post] foo [@field] | +| captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | captured_variables.rb:72:6:72:18 | call to get_field | +| captured_variables.rb:72:6:72:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | captured_variables.rb:72:6:72:18 | call to get_field | | instance_variables.rb:28:20:28:24 | field | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:28:9:28:25 | [post] self [@field] | | instance_variables.rb:33:13:33:13 | x | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:33:9:33:14 | call to new [@field] | +| instance_variables.rb:36:10:36:23 | call to new [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:36:10:36:33 | call to get_field | | instance_variables.rb:36:10:36:23 | call to new [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:36:10:36:33 | call to get_field | | instance_variables.rb:36:14:36:22 | call to taint | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:36:10:36:23 | call to new [@field] | +| instance_variables.rb:39:6:39:23 | call to bar [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:39:6:39:33 | call to get_field | | instance_variables.rb:39:6:39:23 | call to bar [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:39:6:39:33 | call to get_field | | instance_variables.rb:39:14:39:22 | call to taint | instance_variables.rb:31:18:31:18 | x | instance_variables.rb:33:9:33:14 | call to new [@field] | instance_variables.rb:39:6:39:23 | call to bar [@field] | +| instance_variables.rb:54:15:54:23 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:54:1:54:3 | [post] foo [@field] | | instance_variables.rb:54:15:54:23 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:54:1:54:3 | [post] foo [@field] | +| instance_variables.rb:55:6:55:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:55:6:55:18 | call to get_field | | instance_variables.rb:55:6:55:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:55:6:55:18 | call to get_field | +| instance_variables.rb:58:15:58:22 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:58:1:58:3 | [post] bar [@field] | | instance_variables.rb:58:15:58:22 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:58:1:58:3 | [post] bar [@field] | | instance_variables.rb:59:6:59:8 | bar [@field] | instance_variables.rb:16:5:18:7 | self in inc_field [@field] | instance_variables.rb:16:5:18:7 | self in inc_field [@field] | instance_variables.rb:59:6:59:18 | call to inc_field | | instance_variables.rb:59:6:59:8 | bar [@field] | instance_variables.rb:16:5:18:7 | self in inc_field [@field] | instance_variables.rb:17:9:17:14 | [post] self [@field] | instance_variables.rb:59:6:59:18 | call to inc_field | +| instance_variables.rb:67:6:67:9 | foo2 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:67:6:67:19 | call to get_field | | instance_variables.rb:67:6:67:9 | foo2 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:67:6:67:19 | call to get_field | +| instance_variables.rb:70:16:70:24 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:70:1:70:4 | [post] foo3 [@field] | | instance_variables.rb:70:16:70:24 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:70:1:70:4 | [post] foo3 [@field] | +| instance_variables.rb:78:18:78:26 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:78:2:78:5 | [post] foo5 [@field] | | instance_variables.rb:78:18:78:26 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:78:2:78:5 | [post] foo5 [@field] | +| instance_variables.rb:79:6:79:9 | foo5 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:79:6:79:19 | call to get_field | | instance_variables.rb:79:6:79:9 | foo5 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:79:6:79:19 | call to get_field | +| instance_variables.rb:82:32:82:40 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:82:15:82:18 | [post] foo6 [@field] | | instance_variables.rb:82:32:82:40 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:82:15:82:18 | [post] foo6 [@field] | +| instance_variables.rb:83:6:83:9 | foo3 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:83:6:83:19 | call to get_field | | instance_variables.rb:83:6:83:9 | foo3 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:83:6:83:19 | call to get_field | +| instance_variables.rb:84:6:84:9 | foo5 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:84:6:84:19 | call to get_field | | instance_variables.rb:84:6:84:9 | foo5 [@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:19 | call to get_field | +| instance_variables.rb:85:6:85:9 | foo6 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:85:6:85:19 | call to get_field | | instance_variables.rb:85:6:85:9 | foo6 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:85:6:85:19 | call to get_field | +| instance_variables.rb:89:45:89:53 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:89:15:89:18 | [post] foo7 [@field] | +| instance_variables.rb:89:45:89:53 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:89:25:89:28 | [post] foo8 [@field] | | instance_variables.rb:89:45:89:53 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:89:15:89:18 | [post] foo7 [@field] | | instance_variables.rb:89:45:89:53 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:89:25:89:28 | [post] foo8 [@field] | +| instance_variables.rb:90:6:90:9 | foo7 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:90:6:90:19 | call to get_field | | instance_variables.rb:90:6:90:9 | foo7 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:90:6:90:19 | call to get_field | +| instance_variables.rb:91:6:91:9 | foo8 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:91:6:91:19 | call to get_field | | instance_variables.rb:91:6:91:9 | foo8 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:91:6:91:19 | call to get_field | +| instance_variables.rb:95:53:95:61 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:95:22:95:25 | [post] foo9 [@field] | +| instance_variables.rb:95:53:95:61 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:95:32:95:36 | [post] foo10 [@field] | | instance_variables.rb:95:53:95:61 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:95:22:95:25 | [post] foo9 [@field] | | instance_variables.rb:95:53:95:61 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:95:32:95:36 | [post] foo10 [@field] | +| instance_variables.rb:96:6:96:9 | foo9 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:96:6:96:19 | call to get_field | | instance_variables.rb:96:6:96:9 | foo9 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:96:6:96:19 | call to get_field | +| instance_variables.rb:97:6:97:10 | foo10 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:97:6:97:20 | call to get_field | | instance_variables.rb:97:6:97:10 | foo10 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:97:6:97:20 | call to get_field | +| instance_variables.rb:100:17:100:25 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | instance_variables.rb:100:5:100:5 | [post] x [@field] | | instance_variables.rb:100:17:100:25 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | instance_variables.rb:100:5:100:5 | [post] x [@field] | +| instance_variables.rb:105:6:105:10 | foo11 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:105:6:105:20 | call to get_field | | instance_variables.rb:105:6:105:10 | foo11 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:105:6:105:20 | call to get_field | +| instance_variables.rb:109:6:109:10 | foo12 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:109:6:109:20 | call to get_field | | instance_variables.rb:109:6:109:10 | foo12 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:109:6:109:20 | call to get_field | +| instance_variables.rb:114:6:114:10 | foo13 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:114:6:114:20 | call to get_field | | instance_variables.rb:114:6:114: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:114:6:114:20 | call to get_field | | instance_variables.rb:116:17:116:25 | call to taint | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:116:9:116:26 | call to new [@field] | +| instance_variables.rb:117:6:117:10 | foo15 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:117:6:117:20 | call to get_field | | instance_variables.rb:117:6:117:10 | foo15 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:117:6:117:20 | call to get_field | | instance_variables.rb:119:28:119:36 | call to taint | instance_variables.rb:27:25:27:29 | field | instance_variables.rb:28:9:28:25 | [post] self [@field] | instance_variables.rb:119:6:119:10 | [post] foo16 [@field] | +| instance_variables.rb:120:6:120:10 | foo16 [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:120:6:120:20 | call to get_field | | instance_variables.rb:120:6:120:10 | foo16 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:120:6:120:20 | call to get_field | #select -| captured_variables.rb:2:20:2:20 | x | captured_variables.rb:5:20:5:30 | call to source | captured_variables.rb:2:20:2:20 | x | $@ | captured_variables.rb:5:20:5:30 | call to source | call to source | -| captured_variables.rb:23:14:23:14 | x | captured_variables.rb:27:29:27:39 | call to source | captured_variables.rb:23:14:23:14 | x | $@ | captured_variables.rb:27:29:27:39 | call to source | call to source | -| captured_variables.rb:34:14:34:14 | x | captured_variables.rb:38:27:38:37 | call to source | captured_variables.rb:34:14:34:14 | x | $@ | captured_variables.rb:38:27:38:37 | call to source | call to source | +| captured_variables.rb:10:20:10:20 | x | captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:10:20:10:20 | x | $@ | captured_variables.rb:13:20:13:29 | call to taint | call to taint | +| captured_variables.rb:31:14:31:14 | x | captured_variables.rb:35:29:35:38 | call to taint | captured_variables.rb:31:14:31:14 | x | $@ | captured_variables.rb:35:29:35:38 | call to taint | call to taint | +| captured_variables.rb:42:14:42:14 | x | captured_variables.rb:46:27:46:36 | call to taint | captured_variables.rb:42:14:42:14 | x | $@ | captured_variables.rb:46:27:46:36 | call to taint | call to taint | +| captured_variables.rb:50:10:50:10 | x | captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:50:10:50:10 | x | $@ | captured_variables.rb:48:5:48:12 | call to taint | call to taint | +| captured_variables.rb:54:6:54:6 | x | captured_variables.rb:51:9:51:16 | call to taint | captured_variables.rb:54:6:54:6 | x | $@ | captured_variables.rb:51:9:51:16 | call to taint | call to taint | +| captured_variables.rb:72:6:72:18 | call to get_field | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:72:6:72:18 | call to get_field | $@ | captured_variables.rb:66:15:66:22 | call to taint | call to taint | +| captured_variables.rb:87:10:87:10 | y | captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:87:10:87:10 | y | $@ | captured_variables.rb:85:5:85:12 | call to taint | call to taint | +| captured_variables.rb:87:10:87:10 | y | captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:87:10:87:10 | y | $@ | captured_variables.rb:88:9:88:16 | call to taint | call to taint | +| captured_variables.rb:91:6:91:6 | y | captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:91:6:91:6 | y | $@ | captured_variables.rb:88:9:88:16 | call to taint | call to taint | +| captured_variables.rb:105:10:105:10 | x | captured_variables.rb:104:17:104:24 | call to taint | captured_variables.rb:105:10:105:10 | x | $@ | captured_variables.rb:104:17:104:24 | call to taint | call to taint | +| captured_variables.rb:112:18:112:18 | x | captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:112:18:112:18 | x | $@ | captured_variables.rb:109:9:109:17 | call to taint | call to taint | +| captured_variables.rb:112:18:112:18 | x | captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:112:18:112:18 | x | $@ | captured_variables.rb:113:17:113:25 | call to taint | call to taint | +| captured_variables.rb:118:10:118:10 | x | captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:118:10:118:10 | x | $@ | captured_variables.rb:113:17:113:25 | call to taint | call to taint | +| captured_variables.rb:169:18:169:19 | @x | captured_variables.rb:160:14:160:22 | call to taint | captured_variables.rb:169:18:169:19 | @x | $@ | captured_variables.rb:160:14:160:22 | call to taint | call to taint | +| captured_variables.rb:187:18:187:19 | @x | captured_variables.rb:178:14:178:22 | call to taint | captured_variables.rb:187:18:187:19 | @x | $@ | captured_variables.rb:178:14:178:22 | 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:36:10:36:33 | call to get_field | instance_variables.rb:36:14:36:22 | call to taint | instance_variables.rb:36:10:36:33 | call to get_field | $@ | instance_variables.rb:36:14:36:22 | call to taint | call to taint | | instance_variables.rb:39:6:39:33 | call to get_field | instance_variables.rb:39:14:39:22 | call to taint | instance_variables.rb:39:6:39:33 | call to get_field | $@ | instance_variables.rb:39:14:39:22 | call to taint | call to taint | diff --git a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected index 8fbd6f510444..48fed82c4e95 100644 --- a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected +++ b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected @@ -1,7 +1,13 @@ -failures testFailures -| captured_variables.rb:9:14:9:14 | x | Fixed missing result:hasValueFlow=1.2 | -| captured_variables.rb:16:14:16:14 | x | Fixed missing result:hasValueFlow=1.3 | +| captured_variables.rb:17:14:17:14 | x | Fixed missing result:hasValueFlow=1.2 | +| captured_variables.rb:24:14:24:14 | x | Fixed missing result:hasValueFlow=1.3 | +| captured_variables.rb:50:10:50:10 | x | Fixed missing result:hasValueFlow=2 | +| captured_variables.rb:54:6:54:6 | x | Unexpected result: hasValueFlow=1 | +| captured_variables.rb:72:21:72:75 | # $ MISSING: hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 | Fixed spurious result:hasValueFlow=3 | +| captured_variables.rb:91:6:91:6 | y | Unexpected result: hasValueFlow=6 | +| captured_variables.rb:95:14:95:14 | x | Fixed missing result:hasValueFlow=8 | +| captured_variables.rb:118:10:118:10 | x | Unexpected result: hasValueFlow=10 | +| captured_variables.rb:126:14:126:14 | x | Fixed missing result:hasValueFlow=12 | | instance_variables.rb:20:16:20:33 | # $ hasValueFlow=7 | Missing result:hasValueFlow=7 | | instance_variables.rb:36:36:36:54 | # $ hasValueFlow=34 | Missing result:hasValueFlow=34 | | instance_variables.rb:39:36:39:54 | # $ hasValueFlow=35 | Missing result:hasValueFlow=35 | @@ -23,3 +29,4 @@ testFailures | instance_variables.rb:114:23:114:41 | # $ hasValueFlow=28 | Missing result:hasValueFlow=28 | | instance_variables.rb:117:23:117:41 | # $ hasValueFlow=29 | Missing result:hasValueFlow=29 | | instance_variables.rb:120:23:120:41 | # $ hasValueFlow=30 | Missing result:hasValueFlow=30 | +failures diff --git a/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb index 88b5be2e7c30..2a165b3b3c16 100644 --- a/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb +++ b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb @@ -1,22 +1,30 @@ +def taint x + x +end + +def sink x + puts "SINK: #{x}" +end + def capture_local_call x fn = -> { sink(x) } # $ hasValueFlow=1.1 fn.call end -capture_local_call source(1.1) +capture_local_call taint(1.1) def capture_escape_return1 x -> { sink(x) # $ MISSING: hasValueFlow=1.2 } end -(capture_escape_return1 source(1.2)).call +(capture_escape_return1 taint(1.2)).call def capture_escape_return2 x -> { sink(x) # $ MISSING: hasValueFlow=1.3 } end -Something.unknownMethod(capture_escape_return2 source(1.3)) +Something.unknownMethod(capture_escape_return2 taint(1.3)) def capture_escape_unknown_call x fn = -> { @@ -24,7 +32,7 @@ def capture_escape_unknown_call x } Something.unknownMethod(fn) end -capture_escape_unknown_call source(1.4) +capture_escape_unknown_call taint(1.4) def call_it fn fn.call @@ -35,4 +43,152 @@ def capture_escape_known_call x } call_it fn end -capture_escape_known_call source(1.5) +capture_escape_known_call taint(1.5) + +x = taint(1) +[1, 2, 3].each do |i| + sink x # $ hasValueFlow=1 $ MISSING: hasValueFlow=2 + x = taint(2) +end + +sink x # $ hasValueFlow=2 + +class Foo + def set_field x + @field = x + end + def get_field + return @field + end +end + +foo = Foo.new +foo.set_field(taint(3)) +[1, 2, 3].each do |i| + sink(foo.get_field) # $ MISSING: hasValueFlow=3 $ MISSING: hasValueFlow=4 + foo.set_field(taint(4)) +end + +sink(foo.get_field) # $ MISSING: hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 + +foo = Foo.new +if (rand() < 0) then + foo = Foo.new +else + [1, 2, 3].each do |i| + foo.set_field(taint(5)) + end +end + +sink(foo.get_field) # $ MISSING: hasValueFlow=5 + +y = taint(6) +fn = -> { + sink(y) # $ hasValueFlow=6 $ SPURIOUS: hasValueFlow=7 + y = taint(7) +} +fn.call +sink(y) # $ hasValueFlow=7 + +def capture_arg x + -> { + sink x # $ MISSING: hasValueFlow=8 + } +end +capture_arg(taint(8)).call + +def call_block_with x + yield x +end + +call_block_with(taint(9)) do |x| + sink x # $ hasValueFlow=9 +end + +def capture_nested + x = taint(10) + middle = -> { + inner = -> { + sink x # $ hasValueFlow=10 $ SPURIOUS: hasValueFlow=11 + x = taint(11) + } + inner.call + } + middle.call + sink x # $ hasValueFlow=11 +end +capture_nested + +def lambdas + x = 123 + + fn1 = -> { + sink x # $ MISSING: hasValueFlow=12 + } + + fn3 = -> { + y = taint(12) + + fn2 = -> { + x = y + } + + fn2 + } + + fn4 = fn3.call() + fn4.call() + fn1.call() +end + +lambdas + +module CaptureModuleSelf + @x = taint(13) + + def self.foo + yield + end + + self.foo do + sink @x # $ MISSING: hasValueFlow=13 + end +end + +class CaptureInstanceSelf1 + def initialize + @x = taint(14) + end + + def bar + yield + end + + def baz + self.bar do + sink @x # $ hasValueFlow=14 + end + end +end + +CaptureInstanceSelf1.new.baz + +class CaptureInstanceSelf2 + def foo + @x = taint(15) + end + + def bar + yield + end + + def baz + self.bar do + sink @x # $ hasValueFlow=15 + end + end +end + +c = CaptureInstanceSelf2.new +c.foo +c.baz From 48e2dcfa35286c096ffdc957c0fbf2027140fce5 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 10 Aug 2023 20:16:50 +0200 Subject: [PATCH 2/5] Ruby: Reimplement flow through captured variables using field flow --- .../DataFlowConsistency.ql | 2 - .../VariablesConsistency.ql | 1 + ruby/ql/lib/codeql/ruby/ast/Call.qll | 11 + .../lib/codeql/ruby/controlflow/CfgNodes.qll | 5 +- .../lib/codeql/ruby/dataflow/FlowSummary.qll | 65 +++ .../dataflow/internal/DataFlowDispatch.qll | 47 +- .../internal/DataFlowImplSpecific.qll | 3 + .../dataflow/internal/DataFlowPrivate.qll | 465 +++++++++++++----- .../ruby/dataflow/internal/DataFlowPublic.qll | 25 +- .../internal/FlowSummaryImplSpecific.qll | 13 +- .../codeql/ruby/dataflow/internal/SsaImpl.qll | 91 +--- .../ql/lib/codeql/ruby/frameworks/Sinatra.qll | 36 +- .../data/internal/ApiGraphModelsSpecific.qll | 2 +- .../ruby/typetracking/TypeTrackerSpecific.qll | 4 +- .../dataflow/array-flow/array-flow.expected | 8 +- .../call-sensitivity.expected | 4 +- .../dataflow/global/Flow.expected | 195 +++++++- .../global/TypeTrackingInlineTest.expected | 14 +- .../dataflow/global/captured_variables.rb | 24 +- .../dataflow/summaries/Summaries.expected | 113 ++--- .../frameworks/sinatra/Sinatra.expected | 88 ++-- .../TemplateInjection.expected | 14 +- .../security/cwe-022/PathInjection.expected | 6 +- .../security/cwe-117/LogInjection.expected | 20 +- 24 files changed, 815 insertions(+), 441 deletions(-) diff --git a/ruby/ql/consistency-queries/DataFlowConsistency.ql b/ruby/ql/consistency-queries/DataFlowConsistency.ql index 6cf0fe9a2826..dcab11b8dc28 100644 --- a/ruby/ql/consistency-queries/DataFlowConsistency.ql +++ b/ruby/ql/consistency-queries/DataFlowConsistency.ql @@ -11,8 +11,6 @@ private module Input implements InputSig { predicate postWithInFlowExclude(Node n) { n instanceof FlowSummaryNode } predicate argHasPostUpdateExclude(ArgumentNode n) { - n instanceof BlockArgumentNode - or n instanceof FlowSummaryNode or n instanceof SynthHashSplatArgumentNode diff --git a/ruby/ql/consistency-queries/VariablesConsistency.ql b/ruby/ql/consistency-queries/VariablesConsistency.ql index ed2183340d9c..0bed61cf2404 100644 --- a/ruby/ql/consistency-queries/VariablesConsistency.ql +++ b/ruby/ql/consistency-queries/VariablesConsistency.ql @@ -1,4 +1,5 @@ import codeql.ruby.ast.Variable +import codeql.ruby.dataflow.internal.DataFlowPrivate::VariableCapture::Flow::ConsistencyChecks query predicate ambiguousVariable(VariableAccess access, Variable variable) { access.getVariable() = variable and diff --git a/ruby/ql/lib/codeql/ruby/ast/Call.qll b/ruby/ql/lib/codeql/ruby/ast/Call.qll index 8bc5f6ff8776..de72b7666242 100644 --- a/ruby/ql/lib/codeql/ruby/ast/Call.qll +++ b/ruby/ql/lib/codeql/ruby/ast/Call.qll @@ -117,6 +117,17 @@ class MethodCall extends Call instanceof MethodCallImpl { */ final Block getBlock() { result = super.getBlockImpl() } + /** + * Gets the block argument of this method call, if any. + * ```rb + * foo(&block) + * ``` + */ + final BlockArgument getBlockArgument() { result = this.getAnArgument() } + + /** Holds if this method call has a block or block argument. */ + final predicate hasBlock() { exists(this.getBlock()) or exists(this.getBlockArgument()) } + /** * Holds if the safe navigation operator (`&.`) is used in this call. * ```rb diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index 1a8c6bcc6079..e37493910b2c 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -202,7 +202,10 @@ module ExprNodes { override LhsExpr getExpr() { result = super.getExpr() } /** Gets a variable used in (or introduced by) this LHS. */ - Variable getAVariable() { result = e.(VariableAccess).getVariable() } + deprecated Variable getAVariable() { result = e.(VariableAccess).getVariable() } + + /** Gets the variable used in (or introduced by) this LHS. */ + Variable getVariable() { result = e.(VariableAccess).getVariable() } } private class AssignExprChildMapping extends ExprChildMapping, AssignExpr { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll index 20185c112a49..6a788f8d75c7 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll @@ -1,6 +1,8 @@ /** Provides classes and predicates for defining flow summaries. */ import codeql.ruby.AST +private import codeql.ruby.CFG +private import codeql.ruby.typetracking.TypeTracker import codeql.ruby.DataFlow private import internal.FlowSummaryImpl as Impl private import internal.DataFlowDispatch @@ -158,3 +160,66 @@ abstract class SimpleSummarizedCallable extends SummarizedCallable { } class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStack; + +/** + * Provides a set of special flow summaries to ensure that callbacks passed into + * library methods will be passed as `self` arguments into themeselves. That is, + * we are assuming that callbacks passed into library methods will be called, which is + * needed for flow through captured variables. + */ +private module LibraryCallbackSummaries { + private predicate libraryCall(CfgNodes::ExprNodes::CallCfgNode call) { + not exists(getTarget(call)) + } + + private class LibraryBlockMethod extends SummarizedCallable { + LibraryBlockMethod() { this = "" } + + final override MethodCall getACall() { + libraryCall(result.getAControlFlowNode()) and + result.hasBlock() + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[block]" and + output = "Argument[block].Parameter[lambda-self]" and + preservesValue = true + } + } + + private DataFlow::LocalSourceNode trackLambdaCreation(TypeTracker t) { + t.start() and + lambdaCreation(result, TLambdaCallKind(), _) + or + exists(TypeTracker t2 | result = trackLambdaCreation(t2).track(t2, t)) and + not result instanceof DataFlow::SelfParameterNode + } + + private predicate libraryCallHasLambdaArg(CfgNodes::ExprNodes::CallCfgNode call, int i) { + exists(CfgNodes::ExprCfgNode arg | + arg = call.getArgument(i) and + arg = trackLambdaCreation(TypeTracker::end()).getALocalUse().asExpr() and + libraryCall(call) and + not arg instanceof CfgNodes::ExprNodes::BlockArgumentCfgNode + ) + } + + private class LibraryLambdaMethod extends SummarizedCallable { + private int i; + + LibraryLambdaMethod() { + this = "" and + i in [0 .. 10] + } + + final override MethodCall getACall() { + libraryCallHasLambdaArg(result.getAControlFlowNode(), i) + } + + override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { + input = "Argument[" + i + "]" and + output = "Argument[" + i + "].Parameter[lambda-self]" and + preservesValue = true + } + } +} diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll index b2517bc31f6a..5f1337f7a5cd 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowDispatch.qll @@ -21,8 +21,7 @@ private class SelfLocalSourceNode extends DataFlow::LocalSourceNode { SelfLocalSourceNode() { self = this.(SelfParameterNodeImpl).getSelfVariable() or - self = this.(SsaSelfDefinitionNode).getVariable() and - not LocalFlow::localFlowSsaParamInput(_, this) + self = this.(SsaSelfDefinitionNode).getVariable() } /** Gets the `self` variable. */ @@ -424,6 +423,7 @@ private module Cached { cached newtype TArgumentPosition = TSelfArgumentPosition() or + TLambdaSelfArgumentPosition() or TBlockArgumentPosition() or TPositionalArgumentPosition(int pos) { exists(Call c | exists(c.getArgument(pos))) @@ -446,6 +446,7 @@ private module Cached { cached newtype TParameterPosition = TSelfParameterPosition() or + TLambdaSelfParameterPosition() or TBlockParameterPosition() or TPositionalParameterPosition(int pos) { pos = any(Parameter p).getPosition() @@ -941,20 +942,24 @@ private module TrackSingletonMethodOnInstanceInput implements CallGraphConstruct private predicate paramReturnFlow( DataFlow::Node nodeFrom, DataFlow::PostUpdateNode nodeTo, StepSummary summary ) { - exists(RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, Expr nodeFromPreExpr | + exists( + RelevantCall call, DataFlow::Node arg, DataFlow::ParameterNode p, + CfgNodes::ExprCfgNode nodeFromPreExpr + | TypeTrackerSpecific::callStep(call, arg, p) and nodeTo.getPreUpdateNode() = arg and summary.toString() = "return" and ( - nodeFromPreExpr = nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr().getExpr() + nodeFromPreExpr = nodeFrom.(DataFlow::PostUpdateNode).getPreUpdateNode().asExpr() or - nodeFromPreExpr = nodeFrom.asExpr().getExpr() and - singletonMethodOnInstance(_, _, nodeFromPreExpr) + nodeFromPreExpr = nodeFrom.asExpr() and + singletonMethodOnInstance(_, _, nodeFromPreExpr.getExpr()) ) | - nodeFromPreExpr = p.getParameter().(NamedParameter).getVariable().getAnAccess() + nodeFromPreExpr = + LocalFlow::getParameterDefNode(p.getParameter()).getDefinitionExt().getARead() or - nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfVariable().getAnAccess() + nodeFromPreExpr = p.(SelfParameterNodeImpl).getSelfDefinition().getARead() ) } @@ -1276,6 +1281,9 @@ class ParameterPosition extends TParameterPosition { /** Holds if this position represents a `self` parameter. */ predicate isSelf() { this = TSelfParameterPosition() } + /** Holds if this position represents a reference to a lambda itself. Only used for tracking flow through captured variables. */ + predicate isLambdaSelf() { this = TLambdaSelfParameterPosition() } + /** Holds if this position represents a block parameter. */ predicate isBlock() { this = TBlockParameterPosition() } @@ -1313,6 +1321,8 @@ class ParameterPosition extends TParameterPosition { string toString() { this.isSelf() and result = "self" or + this.isLambdaSelf() and result = "lambda self" + or this.isBlock() and result = "block" or exists(int pos | this.isPositional(pos) and result = "position " + pos) @@ -1342,6 +1352,9 @@ class ArgumentPosition extends TArgumentPosition { /** Holds if this position represents a `self` argument. */ predicate isSelf() { this = TSelfArgumentPosition() } + /** Holds if this position represents a lambda `self` argument. Only used for tracking flow through captured variables. */ + predicate isLambdaSelf() { this = TLambdaSelfArgumentPosition() } + /** Holds if this position represents a block argument. */ predicate isBlock() { this = TBlockArgumentPosition() } @@ -1374,6 +1387,8 @@ class ArgumentPosition extends TArgumentPosition { string toString() { this.isSelf() and result = "self" or + this.isLambdaSelf() and result = "lambda self" + or this.isBlock() and result = "block" or exists(int pos | this.isPositional(pos) and result = "position " + pos) @@ -1393,16 +1408,24 @@ class ArgumentPosition extends TArgumentPosition { } pragma[nomagic] -private predicate parameterPositionIsNotSelf(ParameterPosition ppos) { not ppos.isSelf() } +private predicate parameterPositionIsNotSelf(ParameterPosition ppos) { + not ppos.isSelf() and + not ppos.isLambdaSelf() +} pragma[nomagic] -private predicate argumentPositionIsNotSelf(ArgumentPosition apos) { not apos.isSelf() } +private predicate argumentPositionIsNotSelf(ArgumentPosition apos) { + not apos.isSelf() and + not apos.isLambdaSelf() +} /** Holds if arguments at position `apos` match parameters at position `ppos`. */ pragma[nomagic] predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { ppos.isSelf() and apos.isSelf() or + ppos.isLambdaSelf() and apos.isLambdaSelf() + or ppos.isBlock() and apos.isBlock() or exists(int pos | ppos.isPositional(pos) and apos.isPositional(pos)) @@ -1441,8 +1464,6 @@ predicate parameterMatch(ParameterPosition ppos, ArgumentPosition apos) { * This is a temporary hook to support technical debt in the Go language; do not use. */ pragma[inline] -predicate golangSpecificParamArgFilter( - DataFlowCall call, DataFlow::ParameterNode p, ArgumentNode arg -) { +predicate golangSpecificParamArgFilter(DataFlowCall call, ParameterNodeImpl p, ArgumentNode arg) { any() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll index 7ee656da8076..abe0366c513f 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll @@ -17,6 +17,9 @@ module RubyDataFlow implements InputSig { import Private import Public + // includes `LambdaSelfParameterNode`, which is not part of the public API + class ParameterNode = Private::ParameterNodeImpl; + predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { Private::isParameterNode(p, c, pos) } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index 03fc2566fe5f..fa1467aa25c2 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -83,13 +83,13 @@ module LocalFlow { def instanceof Ssa::PhiNode or def instanceof SsaImpl::PhiReadNode - //TODO: or def instanceof LocalFlow::UncertainExplicitSsaDefinition } } /** * Holds if `nodeFrom` is a node for SSA definition `def`, which can reach `next`. */ + pragma[nomagic] private predicate localFlowSsaInputFromDef( SsaDefinitionExtNode nodeFrom, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next ) { @@ -102,20 +102,23 @@ module LocalFlow { } /** - * Holds if `exprFrom` is a last read of SSA definition `def`, which + * Holds if `nodeFrom` is a last read of SSA definition `def`, which * can reach `next`. */ + pragma[nomagic] predicate localFlowSsaInputFromRead( - CfgNodes::ExprCfgNode exprFrom, SsaImpl::DefinitionExt def, SsaInputDefinitionExtNode next + SsaImpl::DefinitionExt def, Node nodeFrom, SsaInputDefinitionExtNode next ) { - exists(BasicBlock bb, int i | + exists(BasicBlock bb, int i, CfgNodes::ExprCfgNode exprFrom | SsaImpl::lastRefBeforeRedefExt(def, bb, i, next.getDefinitionExt()) and exprFrom = bb.getNode(i) and - exprFrom.getExpr() instanceof VariableReadAccess + exprFrom.getExpr() instanceof VariableReadAccess and + exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()] ) } /** Gets the SSA definition node corresponding to parameter `p`. */ + pragma[nomagic] SsaDefinitionExtNode getParameterDefNode(NamedParameter p) { exists(BasicBlock bb, int i | bb.getNode(i).getAstNode() = p.getDefiningAccess() and @@ -123,18 +126,14 @@ module LocalFlow { ) } - /** Gets the SSA definition node corresponding to the implicit `self` parameter for `m`. */ - private SsaDefinitionExtNode getSelfParameterDefNode(MethodBase m) { - result.getDefinitionExt().(Ssa::SelfDefinition).getSourceVariable().getDeclaringScope() = m - } - /** * Holds if `nodeFrom` is a parameter node, and `nodeTo` is a corresponding SSA node. */ - predicate localFlowSsaParamInput(Node nodeFrom, SsaDefinitionExtNode nodeTo) { - nodeTo = getParameterDefNode(nodeFrom.(ParameterNodeImpl).getParameter()) + pragma[nomagic] + predicate localFlowSsaParamInput(ParameterNodeImpl nodeFrom, SsaDefinitionExtNode nodeTo) { + nodeTo = getParameterDefNode(nodeFrom.getParameter()) or - nodeTo = getSelfParameterDefNode(nodeFrom.(SelfParameterNodeImpl).getMethod()) + nodeTo.getDefinitionExt() = nodeFrom.(SelfParameterNodeImpl).getSelfDefinition() } /** @@ -147,39 +146,30 @@ module LocalFlow { /** * Holds if there is a local flow step from `nodeFrom` to `nodeTo` involving - * some SSA definition. + * SSA definition `def`. */ - private predicate localSsaFlowStep(Node nodeFrom, Node nodeTo) { - exists(SsaImpl::DefinitionExt def | - // Flow from assignment into SSA definition - def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and - nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def - or - // Flow from SSA definition to first read - def = nodeFrom.(SsaDefinitionExtNode).getDefinitionExt() and - firstReadExt(def, nodeTo.asExpr()) - or - // Flow from read to next read - localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) - or - // Flow into phi (read) SSA definition node from def - localFlowSsaInputFromDef(nodeFrom, def, nodeTo) - ) + pragma[nomagic] + predicate localSsaFlowStep(SsaImpl::DefinitionExt def, Node nodeFrom, Node nodeTo) { + // Flow from assignment into SSA definition + def.(Ssa::WriteDefinition).assigns(nodeFrom.asExpr()) and + nodeTo.(SsaDefinitionExtNode).getDefinitionExt() = def + or + // Flow from SSA definition to first read + def = nodeFrom.(SsaDefinitionExtNode).getDefinitionExt() and + firstReadExt(def, nodeTo.asExpr()) or - localFlowSsaParamInput(nodeFrom, nodeTo) - // TODO - // or - // // Flow into uncertain SSA definition - // exists(LocalFlow::UncertainExplicitSsaDefinition uncertain | - // localFlowSsaInput(nodeFrom, def, uncertain) and - // uncertain = nodeTo.(SsaDefinitionNode).getDefinition() and - // def = uncertain.getPriorDefinition() - // ) + // Flow from post-update read to next read + localSsaFlowStepUseUse(def, nodeFrom.(PostUpdateNode).getPreUpdateNode(), nodeTo) + or + // Flow into phi (read) SSA definition node from def + localFlowSsaInputFromDef(nodeFrom, def, nodeTo) + or + localFlowSsaParamInput(nodeFrom, nodeTo) and + def = nodeTo.(SsaDefinitionExtNode).getDefinitionExt() } + pragma[nomagic] predicate localFlowStepCommon(Node nodeFrom, Node nodeTo) { - localSsaFlowStep(nodeFrom, nodeTo) - or nodeFrom.asExpr() = nodeTo.asExpr().(CfgNodes::ExprNodes::BlockArgumentCfgNode).getValue() or nodeFrom.asExpr() = getALastEvalNode(nodeTo.asExpr()) @@ -218,7 +208,7 @@ module LocalFlow { } } -/** An argument of a call (including qualifier arguments, excluding block arguments). */ +/** An argument of a call (including qualifier arguments and block arguments). */ private class Argument extends CfgNodes::ExprCfgNode { private CfgNodes::ExprNodes::CallCfgNode call; private ArgumentPosition arg; @@ -239,7 +229,11 @@ private class Argument extends CfgNodes::ExprCfgNode { arg.isKeyword(p.getKey().getConstantValue().getSymbol()) ) or - this = call.getReceiver() and arg.isSelf() + this = call.getReceiver() and + arg.isSelf() + or + lambdaCallExpr(call, this) and + arg.isLambdaSelf() or this = call.getAnArgument() and this.getExpr() instanceof HashSplatExpr and @@ -250,6 +244,13 @@ private class Argument extends CfgNodes::ExprCfgNode { this.getExpr() instanceof SplatExpr and arg.isSplat(pos) ) + or + this = call.getAnArgument() and + this.getExpr() instanceof BlockArgument and + arg.isBlock() + or + this = call.getBlock() and + arg.isBlock() } /** Holds if this expression is the `i`th argument of `c`. */ @@ -266,18 +267,129 @@ predicate isNonConstantExpr(CfgNodes::ExprCfgNode n) { /** Provides logic related to captured variables. */ module VariableCapture { - class CapturedVariable extends LocalVariable { - CapturedVariable() { this.isCaptured() } + private import codeql.dataflow.VariableCapture as Shared - CfgScope getCfgScope() { - exists(Scope scope | scope = this.getDeclaringScope() | - result = scope - or - result = scope.(ModuleBase).getCfgScope() - ) + private predicate closureFlowStep(CfgNodes::ExprCfgNode e1, CfgNodes::ExprCfgNode e2) { + e1 = getALastEvalNode(e2) + or + exists(Ssa::Definition def | + def.getARead() = e2 and + def.getAnUltimateDefinition().(Ssa::WriteDefinition).assigns(e1) + ) + } + + private module CaptureInput implements Shared::InputSig { + private import ruby as R + private import codeql.ruby.controlflow.ControlFlowGraph + private import codeql.ruby.controlflow.BasicBlocks as BasicBlocks + + class Location = R::Location; + + class BasicBlock extends BasicBlocks::BasicBlock { + Callable getEnclosingCallable() { result = this.getScope() } + } + + BasicBlock getImmediateBasicBlockDominator(BasicBlock bb) { + result = bb.getImmediateDominator() + } + + BasicBlock getABasicBlockSuccessor(BasicBlock bb) { result = bb.getASuccessor() } + + class CapturedVariable extends LocalVariable { + CapturedVariable() { this.isCaptured() } + + Callable getCallable() { + exists(Scope scope | scope = this.getDeclaringScope() | + result = scope + or + result = scope.(ModuleBase).getCfgScope() + ) + } + } + + abstract class CapturedParameter extends CapturedVariable { + abstract ParameterNode getParameterNode(); + } + + private class CapturedNamedParameter extends CapturedParameter { + private NamedParameter p; + + CapturedNamedParameter() { this = p.getVariable() } + + override ParameterNode getParameterNode() { result.asParameter() = p } + } + + private class CapturedSelfParameter extends CapturedParameter, SelfVariable { + override SelfParameterNode getParameterNode() { this = result.getSelfVariable() } + } + + class Expr extends CfgNodes::ExprCfgNode { + predicate hasCfgNode(BasicBlock bb, int i) { this = bb.getNode(i) } + } + + class VariableWrite extends Expr, CfgNodes::ExprNodes::AssignExprCfgNode { + CapturedVariable v; + + VariableWrite() { v = this.getLhs().getVariable() } + + CapturedVariable getVariable() { result = v } + } + + class VariableRead extends Expr instanceof CfgNodes::ExprNodes::LocalVariableReadAccessCfgNode { + CapturedVariable v; + + VariableRead() { v = super.getVariable() } + + CapturedVariable getVariable() { result = v } + } + + class ClosureExpr extends Expr { + Callable c; + + ClosureExpr() { lambdaCreationExpr(this, _, c) } + + predicate hasBody(Callable body) { body = c } + + predicate hasAliasedAccess(Expr f) { closureFlowStep+(this, f) and not closureFlowStep(f, _) } + } + + class Callable extends CfgScope { + predicate isConstructor() { none() } } } + class CapturedVariable = CaptureInput::CapturedVariable; + + class ClosureExpr = CaptureInput::ClosureExpr; + + module Flow = Shared::Flow; + + private Flow::ClosureNode asClosureNode(Node n) { + result = n.(CaptureNode).getSynthesizedCaptureNode() + or + result.(Flow::ExprNode).getExpr() = n.asExpr() + or + result.(Flow::VariableWriteSourceNode).getVariableWrite().getRhs() = n.asExpr() + or + result.(Flow::ExprPostUpdateNode).getExpr() = n.(PostUpdateNode).getPreUpdateNode().asExpr() + or + result.(Flow::ParameterNode).getParameter().getParameterNode() = n + or + result.(Flow::ThisParameterNode).getCallable() = n.(LambdaSelfParameterNode).getCallable() + } + + predicate storeStep(Node node1, Content::CapturedVariableContent c, Node node2) { + Flow::storeStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2)) + } + + predicate readStep(Node node1, Content::CapturedVariableContent c, Node node2) { + Flow::readStep(asClosureNode(node1), c.getVariable(), asClosureNode(node2)) + } + + predicate valueStep(Node node1, Node node2) { + Flow::localFlowStep(asClosureNode(node1), asClosureNode(node2)) + } + class CapturedSsaDefinitionExt extends SsaImpl::DefinitionExt { CapturedSsaDefinitionExt() { this.getSourceVariable() instanceof CapturedVariable } } @@ -331,6 +443,7 @@ private module Cached { p instanceof SplatParameter } or TSelfParameterNode(MethodBase m) or + TLambdaSelfParameterNode(Callable c) { lambdaCreationExpr(_, _, c) } or TBlockParameterNode(MethodBase m) or TSynthHashSplatParameterNode(DataFlowCallable c) { isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_))) @@ -366,7 +479,8 @@ private module Cached { TSynthSplatArgumentNode(CfgNodes::ExprNodes::CallCfgNode c) { exists(Argument arg, ArgumentPosition pos | pos.isPositional(_) | arg.isArgumentOf(c, pos)) and not exists(Argument arg, ArgumentPosition pos | pos.isSplat(_) | arg.isArgumentOf(c, pos)) - } + } or + TCaptureNode(VariableCapture::Flow::SynthesizedCaptureNode cn) class TSourceParameterNode = TNormalParameterNode or TBlockParameterNode or TSelfParameterNode or @@ -386,21 +500,23 @@ private module Cached { predicate simpleLocalFlowStep(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or - LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) and - not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) - or - // Flow into phi node from read - exists(CfgNodes::ExprCfgNode exprFrom | - LocalFlow::localFlowSsaInputFromRead(exprFrom, _, nodeTo) + exists(SsaImpl::DefinitionExt def | + // captured variables are handled by the shared `VariableCapture` library + not def instanceof VariableCapture::CapturedSsaDefinitionExt | - exprFrom = nodeFrom.asExpr() and + LocalFlow::localSsaFlowStep(def, nodeFrom, nodeTo) + or + LocalFlow::localSsaFlowStepUseUse(def, nodeFrom, nodeTo) and not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) or - exprFrom = nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr() + LocalFlow::localFlowSsaInputFromRead(def, nodeFrom, nodeTo) and + not FlowSummaryImpl::Private::Steps::prohibitsUseUseFlow(nodeFrom, _) ) or FlowSummaryImpl::Private::Steps::summaryLocalStep(nodeFrom.(FlowSummaryNode).getSummaryNode(), nodeTo.(FlowSummaryNode).getSummaryNode(), true) + or + VariableCapture::valueStep(nodeFrom, nodeTo) } /** This is the local flow predicate that is exposed. */ @@ -408,6 +524,8 @@ private module Cached { predicate localFlowStepImpl(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or + LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo) + or LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) or // Simple flow through library code is included in the exposed local @@ -422,13 +540,11 @@ private module Cached { predicate localFlowStepTypeTracker(Node nodeFrom, Node nodeTo) { LocalFlow::localFlowStepCommon(nodeFrom, nodeTo) or + LocalFlow::localSsaFlowStep(_, nodeFrom, nodeTo) + or LocalFlow::localSsaFlowStepUseUse(_, nodeFrom, nodeTo) or - // Flow into phi node from read - exists(CfgNodes::ExprCfgNode exprFrom | - LocalFlow::localFlowSsaInputFromRead(exprFrom, _, nodeTo) and - exprFrom = [nodeFrom.asExpr(), nodeFrom.(PostUpdateNode).getPreUpdateNode().asExpr()] - ) + LocalFlow::localFlowSsaInputFromRead(_, nodeFrom, nodeTo) or VariableCapture::flowInsensitiveStep(nodeFrom, nodeTo) } @@ -527,6 +643,7 @@ private module Cached { name = [input, output].regexpFind("(?<=(^|\\.)Field\\[)[^\\]]+(?=\\])", _, _).trim() ) } or + TCapturedVariableContent(VariableCapture::CapturedVariable v) or // Only used by type-tracking TAttributeName(string name) { name = any(SetterMethodCall c).getTargetName() } @@ -549,7 +666,8 @@ private module Cached { TUnknownElementContentApprox() or TKnownIntegerElementContentApprox() or TKnownElementContentApprox(string approx) { approx = approxKnownElementIndex(_) } or - TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } + TNonElementContentApprox(Content c) { not c instanceof Content::ElementContent } or + TCapturedVariableContentApprox(VariableCapture::CapturedVariable v) } class TElementContent = TKnownElementContent or TUnknownElementContent; @@ -577,6 +695,10 @@ predicate nodeIsHidden(Node n) { n instanceof SynthSplatArgParameterNode or n instanceof SynthSplatParameterElementNode + or + n instanceof LambdaSelfParameterNode + or + n instanceof CaptureNode } /** An SSA definition, viewed as a node in a data flow graph. */ @@ -624,7 +746,7 @@ class CapturedVariableNode extends NodeImpl, TCapturedVariableNode { /** Gets the captured variable represented by this node. */ VariableCapture::CapturedVariable getVariable() { result = variable } - override CfgScope getCfgScope() { result = variable.getCfgScope() } + override CfgScope getCfgScope() { result = variable.getCallable() } override Location getLocationImpl() { result = variable.getLocation() } @@ -653,6 +775,24 @@ class ReturningStatementNode extends NodeImpl, TReturningNode { override string toStringImpl() { result = n.toString() } } +/** + * A synthesized data flow node representing a closure object that tracks + * captured variables. + */ +class CaptureNode extends NodeImpl, TCaptureNode { + private VariableCapture::Flow::SynthesizedCaptureNode cn; + + CaptureNode() { this = TCaptureNode(cn) } + + VariableCapture::Flow::SynthesizedCaptureNode getSynthesizedCaptureNode() { result = cn } + + override CfgScope getCfgScope() { result = cn.getEnclosingCallable() } + + override Location getLocationImpl() { result = cn.getLocation() } + + override string toStringImpl() { result = cn.toString() } +} + private module ParameterNodes { abstract class ParameterNodeImpl extends NodeImpl { abstract Parameter getParameter(); @@ -722,6 +862,11 @@ private module ParameterNodes { final MethodBase getMethod() { result = method } + /** Gets the corresponding SSA `self` definition, if any. */ + Ssa::SelfDefinition getSelfDefinition() { + result.getSourceVariable().getDeclaringScope() = method + } + /** Gets the underlying `self` variable. */ final SelfVariable getSelfVariable() { result.getDeclaringScope() = method } @@ -735,14 +880,43 @@ private module ParameterNodes { override Location getLocationImpl() { result = method.getLocation() } - override string toStringImpl() { result = "self in " + method.toString() } + override string toStringImpl() { result = "self in " + method } + } + + /** + * The value of a lambda itself at function entry, viewed as a node in a data + * flow graph. + * + * This is used for tracking flow through captured variables, and we use a + * separate node and parameter/argument positions in order to distinguish + * "lambda self" from "normal self", as lambdas may also access outer `self` + * variables (through variable capture). + */ + class LambdaSelfParameterNode extends ParameterNodeImpl, TLambdaSelfParameterNode { + private Callable callable; + + LambdaSelfParameterNode() { this = TLambdaSelfParameterNode(callable) } + + final Callable getCallable() { result = callable } + + override Parameter getParameter() { none() } + + override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { + callable = c.asCallable() and pos.isLambdaSelf() + } + + override CfgScope getCfgScope() { result = callable } + + override Location getLocationImpl() { result = callable.getLocation() } + + override string toStringImpl() { result = "lambda self in " + callable } } /** * The value of a block parameter at function entry, viewed as a node in a data * flow graph. */ - class BlockParameterNode extends ParameterNodeImpl, TBlockParameterNode { + class BlockParameterNode extends ParameterNodeImpl, ArgumentNode, TBlockParameterNode { private MethodBase method; BlockParameterNode() { this = TBlockParameterNode(method) } @@ -757,6 +931,20 @@ private module ParameterNodes { c.asCallable() = method and pos.isBlock() } + CfgNodes::ExprNodes::CallCfgNode getAYieldCall() { + this.getMethod() = result.getExpr().(YieldCall).getEnclosingMethod() + } + + // needed for variable capture flow + override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) { + call = this.getAYieldCall() and + pos.isLambdaSelf() + } + + override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { + this.sourceArgumentOf(call.asCall(), pos) + } + override CfgScope getCfgScope() { result = method } override Location getLocationImpl() { @@ -1027,36 +1215,6 @@ private module ArgumentNodes { } } - /** A data-flow node that represents the `self` argument of a call. */ - class SelfArgumentNode extends ExplicitArgumentNode { - SelfArgumentNode() { arg.isArgumentOf(_, any(ArgumentPosition pos | pos.isSelf())) } - } - - /** A data-flow node that represents a block argument. */ - class BlockArgumentNode extends ArgumentNode { - BlockArgumentNode() { - this.asExpr().getExpr() instanceof BlockArgument or - exists(CfgNodes::ExprNodes::CallCfgNode c | c.getBlock() = this.asExpr()) - } - - override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) { - this.sourceArgumentOf(call.asCall(), pos) - } - - override predicate sourceArgumentOf(CfgNodes::ExprNodes::CallCfgNode call, ArgumentPosition pos) { - pos.isBlock() and - ( - this.asExpr() = call.getBlock() - or - exists(CfgNodes::ExprCfgNode arg | - arg = call.getAnArgument() and - this.asExpr() = arg and - arg.getExpr() instanceof BlockArgument - ) - ) - } - } - private class SummaryArgumentNode extends FlowSummaryNode, ArgumentNode { private DataFlowCall call_; private ArgumentPosition pos_; @@ -1335,7 +1493,7 @@ private module OutNodes { import OutNodes -predicate jumpStepTypeTracker(Node pred, Node succ) { +predicate jumpStep(Node pred, Node succ) { succ.asExpr().getExpr().(ConstantReadAccess).getValue() = pred.asExpr().getExpr() or FlowSummaryImpl::Private::Steps::summaryJumpStep(pred.(FlowSummaryNode).getSummaryNode(), @@ -1344,16 +1502,6 @@ predicate jumpStepTypeTracker(Node pred, Node succ) { any(AdditionalJumpStep s).step(pred, succ) } -predicate jumpStep(Node pred, Node succ) { - jumpStepTypeTracker(pred, succ) - or - SsaImpl::captureFlowIn(_, pred.(SsaDefinitionExtNode).getDefinitionExt(), - succ.(SsaDefinitionExtNode).getDefinitionExt()) - or - SsaImpl::captureFlowOut(_, pred.(SsaDefinitionExtNode).getDefinitionExt(), - succ.(SsaDefinitionExtNode).getDefinitionExt()) -} - private ContentSet getKeywordContent(string name) { exists(ConstantValue::ConstantSymbolValue key | result.isSingleton(TKnownElementContent(key)) and @@ -1439,6 +1587,9 @@ predicate storeStep(Node node1, ContentSet c, Node node2) { ) or storeStepCommon(node1, c, node2) + or + VariableCapture::storeStep(node1, any(Content::CapturedVariableContent v | c.isSingleton(v)), + node2) } /** @@ -1487,6 +1638,8 @@ predicate readStep(Node node1, ContentSet c, Node node2) { c = getPositionalContent(e.getReadPosition()) ) or + VariableCapture::readStep(node1, any(Content::CapturedVariableContent v | c.isSingleton(v)), node2) + or readStepCommon(node1, c, node2) } @@ -1521,27 +1674,60 @@ predicate expectsContent(Node n, ContentSet c) { } private newtype TDataFlowType = - TTodoDataFlowType() or - TTodoDataFlowType2() // Add a dummy value to prevent bad functionality-induced joins arising from a type of size 1. + TLambdaDataFlowType(Callable c) { c = any(LambdaSelfParameterNode n).getCallable() } or + TUnknownDataFlowType() class DataFlowType extends TDataFlowType { string toString() { result = "" } } -predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { none() } +predicate typeStrongerThan(DataFlowType t1, DataFlowType t2) { + t1 = TLambdaDataFlowType(_) and + t2 = TUnknownDataFlowType() +} + +private predicate mustHaveLambdaType(CfgNodes::ExprCfgNode e, Callable c) { + exists(VariableCapture::ClosureExpr ce | ce.hasBody(c) | + e = ce or + ce.hasAliasedAccess(e) + ) +} /** Gets the type of `n` used for type pruning. */ -DataFlowType getNodeType(Node n) { result = TTodoDataFlowType() and exists(n) } +DataFlowType getNodeType(Node n) { + result = TLambdaDataFlowType(n.(LambdaSelfParameterNode).getCallable()) + or + exists(Callable c | + mustHaveLambdaType(n.asExpr(), c) and + result = TLambdaDataFlowType(c) + ) + or + not n instanceof LambdaSelfParameterNode and + not mustHaveLambdaType(n.asExpr(), _) and + result = TUnknownDataFlowType() +} /** Gets a string representation of a `DataFlowType`. */ string ppReprType(DataFlowType t) { none() } +pragma[inline] +private predicate compatibleTypesNonSymRefl(DataFlowType t1, DataFlowType t2) { + t1 = TLambdaDataFlowType(_) and + t2 = TUnknownDataFlowType() +} + /** * Holds if `t1` and `t2` are compatible, that is, whether data can flow from * a node of type `t1` to a node of type `t2`. */ pragma[inline] -predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { any() } +predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { + t1 = t2 + or + compatibleTypesNonSymRefl(t1, t2) + or + compatibleTypesNonSymRefl(t2, t1) +} abstract class PostUpdateNodeImpl extends Node { /** Gets the node before the state update. */ @@ -1583,6 +1769,17 @@ private module PostUpdateNodes { override Node getPreUpdateNode() { result = pre } } + + private class CapturePostUpdateNode extends PostUpdateNodeImpl, CaptureNode { + private CaptureNode pre; + + CapturePostUpdateNode() { + VariableCapture::Flow::capturePostUpdateNode(this.getSynthesizedCaptureNode(), + pre.getSynthesizedCaptureNode()) + } + + override Node getPreUpdateNode() { result = pre } + } } private import PostUpdateNodes @@ -1623,22 +1820,27 @@ newtype LambdaCallKind = TLambdaCallKind() /** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ -predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { +private predicate lambdaCreationExpr(CfgNodes::ExprCfgNode creation, LambdaCallKind kind, Callable c) { kind = TYieldCallKind() and - creation.asExpr().getExpr() = c.asCallable().(Block) + creation.getExpr() = c.(Block) or kind = TLambdaCallKind() and ( - creation.asExpr().getExpr() = c.asCallable().(Lambda) + creation.getExpr() = c.(Lambda) or - creation.asExpr() = + creation = any(CfgNodes::ExprNodes::MethodCallCfgNode mc | - c.asCallable() = mc.getBlock().getExpr() and + c = mc.getBlock().getExpr() and isProcCreationCall(mc.getExpr()) ) ) } +/** Holds if `creation` is an expression that creates a lambda of kind `kind` for `c`. */ +predicate lambdaCreation(Node creation, LambdaCallKind kind, DataFlowCallable c) { + lambdaCreationExpr(creation.asExpr(), kind, c.asCallable()) +} + /** Holds if `call` is a call to `lambda`, `proc`, or `Proc.new` */ pragma[nomagic] private predicate isProcCreationCall(MethodCall call) { @@ -1648,20 +1850,24 @@ private predicate isProcCreationCall(MethodCall call) { call.getReceiver().(ConstantReadAccess).getAQualifiedName() = "Proc" } +/** Holds if `mc` is a call to `receiver.call`. */ +private predicate lambdaCallExpr( + CfgNodes::ExprNodes::MethodCallCfgNode mc, CfgNodes::ExprCfgNode receiver +) { + receiver = mc.getReceiver() and + mc.getExpr().getMethodName() = "call" +} + /** * Holds if `call` is a from-source lambda call of kind `kind` where `receiver` * is the lambda expression. */ predicate lambdaSourceCall(CfgNodes::ExprNodes::CallCfgNode call, LambdaCallKind kind, Node receiver) { kind = TYieldCallKind() and - receiver.(BlockParameterNode).getMethod() = call.getExpr().(YieldCall).getEnclosingMethod() + call = receiver.(BlockParameterNode).getAYieldCall() or kind = TLambdaCallKind() and - call = - any(CfgNodes::ExprNodes::MethodCallCfgNode mc | - receiver.asExpr() = mc.getReceiver() and - mc.getExpr().getMethodName() = "call" - ) + lambdaCallExpr(call, receiver.asExpr()) } /** @@ -1687,8 +1893,11 @@ predicate additionalLambdaFlowStep(Node nodeFrom, Node nodeTo, boolean preserves * One example would be to allow flow like `p.foo = p.bar;`, which is disallowed * by default as a heuristic. */ -predicate allowParameterReturnInSelf(ParameterNode p) { +predicate allowParameterReturnInSelf(ParameterNodeImpl p) { FlowSummaryImpl::Private::summaryAllowParameterReturnInSelf(p) + or + VariableCapture::Flow::heuristicAllowInstanceParameterReturnInSelf(p.(SelfParameterNode) + .getCallable()) } /** An approximated `Content`. */ @@ -1765,4 +1974,4 @@ class AdditionalJumpStep extends Unit { * * Argument `arg` is part of a path from a source to a sink, and `p` is the target parameter. */ -int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNode p) { none() } +int getAdditionalFlowIntoCallNodeTerm(ArgumentNode arg, ParameterNodeImpl p) { none() } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 269087b7a079..6548c4ebe477 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -568,6 +568,18 @@ module Content { /** Gets `AttributeNameContent` of the given name. */ AttributeNameContent getAttributeName(string name) { result.getName() = name } + + /** A captured variable. */ + class CapturedVariableContent extends Content, TCapturedVariableContent { + private LocalVariable v; + + CapturedVariableContent() { this = TCapturedVariableContent(v) } + + /** Gets the captured variable. */ + LocalVariable getVariable() { result = v } + + override string toString() { result = "captured " + v } + } } /** @@ -765,14 +777,14 @@ module BarrierGuard { * This is restricted to calls where the variable is captured inside a * block. */ - private Ssa::Definition getAMaybeGuardedCapturedDef() { + private Ssa::CapturedEntryDefinition getAMaybeGuardedCapturedDef() { exists( CfgNodes::ExprCfgNode g, boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def, CfgNodes::ExprNodes::CallCfgNode call | def.getARead() = testedNode and guardChecks(g, testedNode, branch) and - SsaImpl::captureFlowIn(call, def, result) and + def.getSourceVariable() = result.getSourceVariable() and guardControlsBlock(g, call.getBasicBlock(), branch) and result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock() ) @@ -830,14 +842,14 @@ abstract deprecated class BarrierGuard extends CfgNodes::ExprCfgNode { * This is restricted to calls where the variable is captured inside a * block. */ - private Ssa::Definition getAMaybeGuardedCapturedDef() { + private Ssa::CapturedEntryDefinition getAMaybeGuardedCapturedDef() { exists( boolean branch, CfgNodes::ExprCfgNode testedNode, Ssa::Definition def, CfgNodes::ExprNodes::CallCfgNode call | def.getARead() = testedNode and this.checks(testedNode, branch) and - SsaImpl::captureFlowIn(call, def, result) and + def.getSourceVariable() = result.getSourceVariable() and this.controlsBlock(call.getBasicBlock(), branch) and result.getBasicBlock().getScope() = call.getExpr().(MethodCall).getBlock() ) @@ -1208,7 +1220,10 @@ class LhsExprNode extends ExprNode { LhsExpr asLhsExprAstNode() { result = lhsExprCfgNode.getExpr() } /** Gets a variable used in (or introduced by) this LHS. */ - Variable getAVariable() { result = lhsExprCfgNode.getAVariable() } + deprecated Variable getAVariable() { result = lhsExprCfgNode.getAVariable() } + + /** Gets the variable used in (or introduced by) this LHS. */ + Variable getVariable() { result = lhsExprCfgNode.getVariable() } } /** diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll index 07a68984820d..c0098ec2f21d 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/FlowSummaryImplSpecific.qll @@ -24,7 +24,7 @@ class NeutralCallableBase = string; DataFlowCallable inject(SummarizedCallable c) { result.asLibraryCallable() = c } /** Gets the parameter position representing a callback itself, if any. */ -ArgumentPosition callbackSelfParameterPosition() { none() } // disables implicit summary flow to `self` for callbacks +ArgumentPosition callbackSelfParameterPosition() { result.isLambdaSelf() } /** Gets the synthesized data-flow call for `receiver`. */ SummaryCall summaryDataFlowCall(SummaryNode receiver) { receiver = result.getReceiver() } @@ -215,6 +215,9 @@ string getParameterPosition(ParameterPosition pos) { pos.isSelf() and result = "self" or + pos.isLambdaSelf() and + result = "lambda-self" + or pos.isBlock() and result = "block" or @@ -232,6 +235,8 @@ string getParameterPosition(ParameterPosition pos) { string getArgumentPosition(ArgumentPosition pos) { pos.isSelf() and result = "self" or + pos.isLambdaSelf() and result = "lambda-self" + or pos.isBlock() and result = "block" or exists(int i | @@ -372,6 +377,9 @@ ArgumentPosition parseParamBody(string s) { s = "self" and result.isSelf() or + s = "lambda-self" and + result.isLambdaSelf() + or s = "block" and result.isBlock() or @@ -402,6 +410,9 @@ ParameterPosition parseArgBody(string s) { s = "self" and result.isSelf() or + s = "lambda-self" and + result.isLambdaSelf() + or s = "block" and result.isBlock() or diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll index 9502f04a7eb6..771c0f0bc393 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/SsaImpl.qll @@ -363,97 +363,8 @@ private module Cached { ) } - pragma[noinline] - private predicate defReachesCallReadInOuterScope( - Definition def, CallCfgNode call, LocalVariable v, Cfg::CfgScope scope - ) { - exists(Cfg::BasicBlock bb, int i | - Impl::ssaDefReachesRead(v, def, bb, i) and - capturedCallRead(call, bb, i, v) and - scope.getOuterCfgScope() = bb.getScope() - ) - } - - pragma[noinline] - private predicate hasCapturedEntryWrite(Definition entry, LocalVariable v, Cfg::CfgScope scope) { - exists(Cfg::BasicBlock bb, int i | - capturedEntryWrite(bb, i, v) and - entry.definesAt(v, bb, i) and - bb.getScope().getOuterCfgScope*() = scope - ) - } - - /** - * Holds if there is flow for a captured variable from the enclosing scope into a block. - * ```rb - * foo = 0 - * bar { - * puts foo - * } - * ``` - */ - cached - predicate captureFlowIn(CallCfgNode call, Definition def, Definition entry) { - exists(LocalVariable v, Cfg::CfgScope scope | - defReachesCallReadInOuterScope(def, call, v, scope) and - hasCapturedEntryWrite(entry, v, scope) - | - // If the read happens inside a block, we restrict to the call that - // contains the block - not scope instanceof Block - or - scope = call.getExpr().(MethodCall).getBlock() - ) - } - private import codeql.ruby.dataflow.SSA - pragma[noinline] - private predicate defReachesExitReadInInnerScope( - Definition def, LocalVariable v, Cfg::CfgScope scope - ) { - exists(Cfg::BasicBlock bb, int i | - Impl::ssaDefReachesRead(v, def, bb, i) and - capturedExitRead(bb, i, v) and - scope = bb.getScope().getOuterCfgScope*() - ) - } - - pragma[noinline] - private predicate hasCapturedExitRead( - Definition exit, CallCfgNode call, LocalVariable v, Cfg::CfgScope scope - ) { - exists(Cfg::BasicBlock bb, int i | - capturedCallWrite(call, bb, i, v) and - exit.definesAt(v, bb, i) and - bb.getScope() = scope.getOuterCfgScope() - ) - } - - /** - * Holds if there is outgoing flow for a captured variable that is updated in a block. - * ```rb - * foo = 0 - * bar { - * foo += 10 - * } - * puts foo - * ``` - */ - cached - predicate captureFlowOut(CallCfgNode call, Definition def, Definition exit) { - exists(LocalVariable v, Cfg::CfgScope scope | - defReachesExitReadInInnerScope(def, v, scope) and - hasCapturedExitRead(exit, call, v, _) - | - // If the read happens inside a block, we restrict to the call that - // contains the block - not scope instanceof Block - or - scope = call.getExpr().(MethodCall).getBlock() - ) - } - cached Definition phiHasInputFromBlock(PhiNode phi, Cfg::BasicBlock bb) { Impl::phiHasInputFromBlock(phi, result, bb) @@ -570,6 +481,8 @@ import Cached * Only intended for internal use. */ class DefinitionExt extends Impl::DefinitionExt { + VariableReadAccessCfgNode getARead() { result = getARead(this) } + override string toString() { result = this.(Ssa::Definition).toString() } /** Gets the location of this definition. */ diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll index ef9a93925274..29bcb32a444a 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll @@ -273,39 +273,19 @@ module Sinatra { filter.getApp() = route.getApp() and // the filter applies to all routes not filter.hasPattern() and - selfPostUpdate(pred, filter.getApp(), filter.getBody().asExpr().getExpr()) and - blockCapturedSelfParameterNode(succ, route.getBody().asExpr().getExpr()) + blockPostUpdate(pred, filter.getBody()) and + blockSelfParameterNode(succ, route.getBody().asExpr().getExpr()) ) } } - /** - * Holds if `n` is a post-update node for the `self` parameter of `app` in block `b`. - * - * In this example, `n` is the post-update node for `@foo = 1`. - * ```rb - * class MyApp < Sinatra::Base - * before do - * @foo = 1 - * end - * end - * ``` - */ - private predicate selfPostUpdate(DataFlow::PostUpdateNode n, App app, Block b) { - n.getPreUpdateNode().asExpr().getExpr() = - any(SelfVariableAccess self | - pragma[only_bind_into](b) = self.getEnclosingCallable() and - self.getVariable().getDeclaringScope() = app.getADeclaration() - ) + /** Holds if `n` is a post-update node for the block `b`. */ + private predicate blockPostUpdate(DataFlow::PostUpdateNode n, DataFlow::BlockNode b) { + n.getPreUpdateNode() = b } - /** - * Holds if `n` is a node representing the `self` parameter captured by block `b`. - */ - private predicate blockCapturedSelfParameterNode(DataFlow::Node n, Block b) { - exists(Ssa::CapturedSelfDefinition d | - n.(DataFlowPrivate::SsaDefinitionExtNode).getDefinitionExt() = d and - d.getBasicBlock().getScope() = b - ) + /** Holds if `n` is a `self` parameter belonging to block `b`. */ + private predicate blockSelfParameterNode(DataFlowPrivate::LambdaSelfParameterNode n, Block b) { + n.getCallable() = b } } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll index 7247409612d6..eec603ad78ba 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/data/internal/ApiGraphModelsSpecific.qll @@ -243,7 +243,7 @@ predicate isExtraValidTokenArgumentInIdentifyingAccessPath(string name, string a or name = ["Argument", "Parameter"] and ( - argument = ["self", "block", "any", "any-named"] + argument = ["self", "lambda-self", "block", "any", "any-named"] or argument.regexpMatch("\\w+:") // keyword argument ) diff --git a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll index da621de5edb6..7a19210739a8 100644 --- a/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/typetracking/TypeTrackerSpecific.qll @@ -6,7 +6,6 @@ private import codeql.ruby.dataflow.internal.DataFlowImplCommon as DataFlowImplC private import codeql.ruby.dataflow.internal.DataFlowPublic as DataFlowPublic private import codeql.ruby.dataflow.internal.DataFlowPrivate as DataFlowPrivate private import codeql.ruby.dataflow.internal.DataFlowDispatch as DataFlowDispatch -private import codeql.ruby.dataflow.internal.SsaImpl as SsaImpl private import codeql.ruby.dataflow.internal.FlowSummaryImpl as FlowSummaryImpl private import codeql.ruby.dataflow.internal.FlowSummaryImplSpecific as FlowSummaryImplSpecific private import codeql.ruby.dataflow.internal.AccessPathSyntax @@ -74,7 +73,7 @@ predicate simpleLocalFlowStep = DataFlowPrivate::localFlowStepTypeTracker/2; /** * Holds if data can flow from `node1` to `node2` in a way that discards call contexts. */ -predicate jumpStep = DataFlowPrivate::jumpStepTypeTracker/2; +predicate jumpStep = DataFlowPrivate::jumpStep/2; /** Holds if there is direct flow from `param` to a return. */ pragma[nomagic] @@ -180,6 +179,7 @@ private predicate argumentPositionMatch( ) { exists(DataFlowDispatch::ArgumentPosition apos | arg.sourceArgumentOf(call, apos) and + not apos.isLambdaSelf() and DataFlowDispatch::parameterMatch(ppos, apos) ) } diff --git a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected index c52399b3914a..0408a194c993 100644 --- a/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected +++ b/ruby/ql/test/library-tests/dataflow/array-flow/array-flow.expected @@ -450,10 +450,10 @@ edges | array_flow.rb:403:5:403:5 | a [element 2] | array_flow.rb:404:18:404:18 | a [element 2] | | array_flow.rb:403:16:403:25 | call to source | array_flow.rb:403:5:403:5 | a [element 2] | | array_flow.rb:404:5:404:5 | b [element 2] | array_flow.rb:408:10:408:10 | b [element 2] | -| array_flow.rb:404:9:406:7 | __synth__0__1 | array_flow.rb:404:13:404:13 | x | -| array_flow.rb:404:13:404:13 | x | array_flow.rb:405:14:405:14 | x | -| array_flow.rb:404:13:404:13 | x | array_flow.rb:407:10:407:10 | x | +| array_flow.rb:404:9:406:7 | [post] { ... } [captured x] | array_flow.rb:407:10:407:10 | x | +| array_flow.rb:404:9:406:7 | __synth__0__1 | array_flow.rb:405:14:405:14 | x | | array_flow.rb:404:18:404:18 | a [element 2] | array_flow.rb:404:5:404:5 | b [element 2] | +| array_flow.rb:404:18:404:18 | a [element 2] | array_flow.rb:404:9:406:7 | [post] { ... } [captured x] | | array_flow.rb:404:18:404:18 | a [element 2] | array_flow.rb:404:9:406:7 | __synth__0__1 | | array_flow.rb:408:10:408:10 | b [element 2] | array_flow.rb:408:10:408:13 | ...[...] | | array_flow.rb:412:5:412:5 | a [element 2] | array_flow.rb:413:5:413:5 | a [element 2] | @@ -2609,8 +2609,8 @@ nodes | array_flow.rb:403:5:403:5 | a [element 2] | semmle.label | a [element 2] | | array_flow.rb:403:16:403:25 | call to source | semmle.label | call to source | | array_flow.rb:404:5:404:5 | b [element 2] | semmle.label | b [element 2] | +| array_flow.rb:404:9:406:7 | [post] { ... } [captured x] | semmle.label | [post] { ... } [captured x] | | array_flow.rb:404:9:406:7 | __synth__0__1 | semmle.label | __synth__0__1 | -| array_flow.rb:404:13:404:13 | x | semmle.label | x | | array_flow.rb:404:18:404:18 | a [element 2] | semmle.label | a [element 2] | | array_flow.rb:405:14:405:14 | x | semmle.label | x | | array_flow.rb:407:10:407:10 | x | semmle.label | x | diff --git a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected index 4563c900177c..a81205d462bf 100644 --- a/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected +++ b/ruby/ql/test/library-tests/dataflow/call-sensitivity/call-sensitivity.expected @@ -32,7 +32,8 @@ edges | call_sensitivity.rb:66:20:66:20 | x | call_sensitivity.rb:67:24:67:24 | x | | call_sensitivity.rb:67:24:67:24 | x | call_sensitivity.rb:62:18:62:18 | y | | call_sensitivity.rb:70:30:70:30 | x | call_sensitivity.rb:71:10:71:10 | x | -| call_sensitivity.rb:74:18:74:18 | y | call_sensitivity.rb:76:17:76:17 | y | +| call_sensitivity.rb:74:18:74:18 | y | call_sensitivity.rb:75:20:77:7 | do ... end [captured y] | +| call_sensitivity.rb:75:20:77:7 | do ... end [captured y] | call_sensitivity.rb:76:17:76:17 | y | | call_sensitivity.rb:76:17:76:17 | y | call_sensitivity.rb:50:15:50:15 | x | | call_sensitivity.rb:80:15:80:15 | x | call_sensitivity.rb:81:18:81:18 | x | | call_sensitivity.rb:81:18:81:18 | x | call_sensitivity.rb:50:15:50:15 | x | @@ -121,6 +122,7 @@ nodes | call_sensitivity.rb:70:30:70:30 | x | semmle.label | x | | call_sensitivity.rb:71:10:71:10 | x | semmle.label | x | | call_sensitivity.rb:74:18:74:18 | y | semmle.label | y | +| call_sensitivity.rb:75:20:77:7 | do ... end [captured y] | semmle.label | do ... end [captured y] | | call_sensitivity.rb:76:17:76:17 | y | semmle.label | y | | call_sensitivity.rb:80:15:80:15 | x | semmle.label | x | | call_sensitivity.rb:81:18:81:18 | x | semmle.label | x | diff --git a/ruby/ql/test/library-tests/dataflow/global/Flow.expected b/ruby/ql/test/library-tests/dataflow/global/Flow.expected index 523259015973..5c240c152d51 100644 --- a/ruby/ql/test/library-tests/dataflow/global/Flow.expected +++ b/ruby/ql/test/library-tests/dataflow/global/Flow.expected @@ -1,49 +1,115 @@ testFailures edges -| captured_variables.rb:9:24:9:24 | x | captured_variables.rb:10:20:10:20 | x | +| captured_variables.rb:9:24:9:24 | x | captured_variables.rb:10:10:10:23 | -> { ... } [captured x] | +| captured_variables.rb:9:24:9:24 | x | captured_variables.rb:11:5:11:6 | fn [captured x] | +| captured_variables.rb:10:5:10:6 | fn [captured x] | captured_variables.rb:11:5:11:6 | fn [captured x] | +| captured_variables.rb:10:10:10:23 | -> { ... } [captured x] | captured_variables.rb:10:5:10:6 | fn [captured x] | +| captured_variables.rb:11:5:11:6 | fn [captured x] | captured_variables.rb:10:20:10:20 | x | | captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:9:24:9:24 | x | -| captured_variables.rb:29:33:29:33 | x | captured_variables.rb:31:14:31:14 | x | +| captured_variables.rb:15:28:15:28 | x | captured_variables.rb:16:5:18:5 | -> { ... } [captured x] | +| captured_variables.rb:20:1:20:35 | ( ... ) [captured x] | captured_variables.rb:17:14:17:14 | x | +| captured_variables.rb:20:2:20:34 | call to capture_escape_return1 [captured x] | captured_variables.rb:20:1:20:35 | ( ... ) [captured x] | +| captured_variables.rb:20:25:20:34 | call to taint | captured_variables.rb:15:28:15:28 | x | +| captured_variables.rb:20:25:20:34 | call to taint | captured_variables.rb:20:2:20:34 | call to capture_escape_return1 [captured x] | +| captured_variables.rb:22:28:22:28 | x | captured_variables.rb:23:5:25:5 | -> { ... } [captured x] | +| captured_variables.rb:27:25:27:57 | call to capture_escape_return2 [captured x] | captured_variables.rb:24:14:24:14 | x | +| captured_variables.rb:27:48:27:57 | call to taint | captured_variables.rb:22:28:22:28 | x | +| captured_variables.rb:27:48:27:57 | call to taint | captured_variables.rb:27:25:27:57 | call to capture_escape_return2 [captured x] | +| captured_variables.rb:29:33:29:33 | x | captured_variables.rb:30:10:32:5 | -> { ... } [captured x] | +| captured_variables.rb:29:33:29:33 | x | captured_variables.rb:33:29:33:30 | fn [captured x] | +| captured_variables.rb:30:5:30:6 | fn [captured x] | captured_variables.rb:33:29:33:30 | fn [captured x] | +| captured_variables.rb:30:10:32:5 | -> { ... } [captured x] | captured_variables.rb:30:5:30:6 | fn [captured x] | +| captured_variables.rb:33:29:33:30 | fn [captured x] | captured_variables.rb:31:14:31:14 | x | | captured_variables.rb:35:29:35:38 | call to taint | captured_variables.rb:29:33:29:33 | x | -| captured_variables.rb:40:31:40:31 | x | captured_variables.rb:42:14:42:14 | x | +| captured_variables.rb:37:13:37:14 | fn [captured x] | captured_variables.rb:38:5:38:6 | fn [captured x] | +| captured_variables.rb:38:5:38:6 | fn [captured x] | captured_variables.rb:42:14:42:14 | x | +| captured_variables.rb:40:31:40:31 | x | captured_variables.rb:41:10:43:5 | -> { ... } [captured x] | +| captured_variables.rb:40:31:40:31 | x | captured_variables.rb:44:13:44:14 | fn [captured x] | +| captured_variables.rb:41:5:41:6 | fn [captured x] | captured_variables.rb:44:13:44:14 | fn [captured x] | +| captured_variables.rb:41:10:43:5 | -> { ... } [captured x] | captured_variables.rb:41:5:41:6 | fn [captured x] | +| captured_variables.rb:44:13:44:14 | fn [captured x] | captured_variables.rb:37:13:37:14 | fn [captured x] | | captured_variables.rb:46:27:46:36 | call to taint | captured_variables.rb:40:31:40:31 | x | -| captured_variables.rb:48:1:48:1 | x | captured_variables.rb:50:10:50:10 | x | -| captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:48:1:48:1 | x | -| captured_variables.rb:51:5:51:5 | x | captured_variables.rb:54:6:54:6 | x | -| captured_variables.rb:51:9:51:16 | call to taint | captured_variables.rb:51:5:51:5 | x | +| captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:49:16:52:3 | do ... end [captured x] | +| captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:54:6:54:6 | x | +| captured_variables.rb:49:16:52:3 | [post] do ... end [captured x] | captured_variables.rb:54:6:54:6 | x | +| captured_variables.rb:49:16:52:3 | do ... end [captured x] | captured_variables.rb:50:10:50:10 | x | +| captured_variables.rb:51:9:51:16 | call to taint | captured_variables.rb:49:16:52:3 | [post] do ... end [captured x] | | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:18:58:18 | x | | captured_variables.rb:58:18:58:18 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:16:61:21 | self [@field] | | captured_variables.rb:61:16:61:21 | @field | captured_variables.rb:61:9:61:21 | return | | captured_variables.rb:61:16:61:21 | self [@field] | captured_variables.rb:61:16:61:21 | @field | +| captured_variables.rb:66:1:66:3 | [post] foo [@field] | captured_variables.rb:67:16:70:3 | do ... end [captured foo, @field] | | captured_variables.rb:66:1:66:3 | [post] foo [@field] | captured_variables.rb:72:6:72:8 | foo [@field] | | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:57:19:57:19 | x | | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:66:1:66:3 | [post] foo [@field] | | captured_variables.rb:66:15:66:22 | call to taint | instance_variables.rb:10:19:10:19 | x | +| captured_variables.rb:67:16:70:3 | [post] do ... end [captured foo, @field] | captured_variables.rb:72:6:72:8 | foo [@field] | +| captured_variables.rb:67:16:70:3 | do ... end [captured foo, @field] | captured_variables.rb:68:10:68:12 | foo [@field] | +| captured_variables.rb:68:10:68:12 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | +| captured_variables.rb:68:10:68:12 | foo [@field] | captured_variables.rb:68:10:68:22 | call to get_field | +| captured_variables.rb:68:10:68:12 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | +| captured_variables.rb:69:5:69:7 | [post] foo [@field] | captured_variables.rb:67:16:70:3 | [post] do ... end [captured foo, @field] | +| captured_variables.rb:69:19:69:26 | call to taint | captured_variables.rb:57:19:57:19 | x | +| captured_variables.rb:69:19:69:26 | call to taint | captured_variables.rb:69:5:69:7 | [post] foo [@field] | +| captured_variables.rb:69:19:69:26 | call to taint | instance_variables.rb:10:19:10:19 | x | | captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | | captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:72:6:72:18 | call to get_field | | captured_variables.rb:72:6:72:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | -| captured_variables.rb:85:1:85:1 | y | captured_variables.rb:87:10:87:10 | y | -| captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:85:1:85:1 | y | -| captured_variables.rb:88:5:88:5 | y | captured_variables.rb:87:10:87:10 | y | -| captured_variables.rb:88:5:88:5 | y | captured_variables.rb:91:6:91:6 | y | -| captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:88:5:88:5 | y | +| captured_variables.rb:78:20:80:7 | [post] do ... end [captured foo, @field] | captured_variables.rb:83:6:83:8 | foo [@field] | +| captured_variables.rb:79:9:79:11 | [post] foo [@field] | captured_variables.rb:78:20:80:7 | [post] do ... end [captured foo, @field] | +| captured_variables.rb:79:23:79:30 | call to taint | captured_variables.rb:57:19:57:19 | x | +| captured_variables.rb:79:23:79:30 | call to taint | captured_variables.rb:79:9:79:11 | [post] foo [@field] | +| captured_variables.rb:79:23:79:30 | call to taint | instance_variables.rb:10:19:10:19 | x | +| captured_variables.rb:83:6:83:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | +| captured_variables.rb:83:6:83:8 | foo [@field] | captured_variables.rb:83:6:83:18 | call to get_field | +| captured_variables.rb:83:6:83:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | +| captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:86:6:89:1 | -> { ... } [captured y] | +| captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:90:1:90:2 | fn [captured y] | +| captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:91:6:91:6 | y | +| captured_variables.rb:86:1:86:2 | fn [captured y] | captured_variables.rb:90:1:90:2 | fn [captured y] | +| captured_variables.rb:86:6:89:1 | -> { ... } [captured y] | captured_variables.rb:86:1:86:2 | fn [captured y] | +| captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:90:1:90:2 | [post] fn [captured y] | +| captured_variables.rb:90:1:90:2 | [post] fn [captured y] | captured_variables.rb:91:6:91:6 | y | +| captured_variables.rb:90:1:90:2 | fn [captured y] | captured_variables.rb:87:10:87:10 | y | +| captured_variables.rb:93:17:93:17 | x | captured_variables.rb:94:5:96:5 | -> { ... } [captured x] | +| captured_variables.rb:98:1:98:21 | call to capture_arg [captured x] | captured_variables.rb:95:14:95:14 | x | +| captured_variables.rb:98:13:98:20 | call to taint | captured_variables.rb:93:17:93:17 | x | +| captured_variables.rb:98:13:98:20 | call to taint | captured_variables.rb:98:1:98:21 | call to capture_arg [captured x] | | captured_variables.rb:100:21:100:21 | x | captured_variables.rb:101:11:101:11 | x | | captured_variables.rb:101:11:101:11 | x | captured_variables.rb:104:31:104:31 | x | | captured_variables.rb:104:17:104:24 | call to taint | captured_variables.rb:100:21:100:21 | x | | captured_variables.rb:104:31:104:31 | x | captured_variables.rb:105:10:105:10 | x | -| captured_variables.rb:109:5:109:5 | x | captured_variables.rb:112:18:112:18 | x | -| captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:109:5:109:5 | x | -| captured_variables.rb:113:13:113:13 | x | captured_variables.rb:112:18:112:18 | x | -| captured_variables.rb:113:13:113:13 | x | captured_variables.rb:118:10:118:10 | x | -| captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:113:13:113:13 | x | +| captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:110:14:116:5 | -> { ... } [captured x] | +| captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:117:5:117:10 | middle [captured x] | +| captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:118:10:118:10 | x | +| captured_variables.rb:110:5:110:10 | middle [captured x] | captured_variables.rb:117:5:117:10 | middle [captured x] | +| captured_variables.rb:110:14:116:5 | -> { ... } [captured x] | captured_variables.rb:110:5:110:10 | middle [captured x] | +| captured_variables.rb:111:9:111:13 | inner [captured x] | captured_variables.rb:115:9:115:13 | inner [captured x] | +| captured_variables.rb:111:17:114:9 | -> { ... } [captured x] | captured_variables.rb:111:9:111:13 | inner [captured x] | +| captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:115:9:115:13 | [post] inner [captured x] | +| captured_variables.rb:115:9:115:13 | [post] inner [captured x] | captured_variables.rb:117:5:117:10 | [post] middle [captured x] | +| captured_variables.rb:115:9:115:13 | inner [captured x] | captured_variables.rb:112:18:112:18 | x | +| captured_variables.rb:117:5:117:10 | [post] middle [captured x] | captured_variables.rb:118:10:118:10 | x | +| captured_variables.rb:117:5:117:10 | middle [captured x] | captured_variables.rb:111:17:114:9 | -> { ... } [captured x] | +| captured_variables.rb:117:5:117:10 | middle [captured x] | captured_variables.rb:115:9:115:13 | inner [captured x] | +| captured_variables.rb:147:5:147:6 | [post] self [@x] | captured_variables.rb:153:14:155:7 | do ... end [captured self, @x] | +| captured_variables.rb:147:10:147:18 | call to taint | captured_variables.rb:147:5:147:6 | [post] self [@x] | +| captured_variables.rb:149:5:151:7 | &block [captured self, @x] | captured_variables.rb:154:14:154:15 | self [@x] | +| captured_variables.rb:153:14:155:7 | do ... end [captured self, @x] | captured_variables.rb:149:5:151:7 | &block [captured self, @x] | +| captured_variables.rb:154:14:154:15 | self [@x] | captured_variables.rb:154:14:154:15 | @x | | captured_variables.rb:160:9:160:10 | [post] self [@x] | captured_variables.rb:174:1:174:24 | call to new [@x] | | captured_variables.rb:160:14:160:22 | call to taint | captured_variables.rb:160:9:160:10 | [post] self [@x] | -| captured_variables.rb:167:5:171:7 | self in baz [@x] | captured_variables.rb:169:18:169:19 | self [@x] | +| captured_variables.rb:163:5:165:7 | &block [captured self, @x] | captured_variables.rb:169:18:169:19 | self [@x] | +| captured_variables.rb:167:5:171:7 | self in baz [@x] | captured_variables.rb:168:18:170:11 | do ... end [captured self, @x] | +| captured_variables.rb:168:18:170:11 | do ... end [captured self, @x] | captured_variables.rb:163:5:165:7 | &block [captured self, @x] | | captured_variables.rb:169:18:169:19 | self [@x] | captured_variables.rb:169:18:169:19 | @x | | captured_variables.rb:174:1:174:24 | call to new [@x] | captured_variables.rb:167:5:171:7 | self in baz [@x] | | captured_variables.rb:178:9:178:10 | [post] self [@x] | captured_variables.rb:193:1:193:1 | [post] c [@x] | | captured_variables.rb:178:14:178:22 | call to taint | captured_variables.rb:178:9:178:10 | [post] self [@x] | -| captured_variables.rb:185:5:189:7 | self in baz [@x] | captured_variables.rb:187:18:187:19 | self [@x] | +| captured_variables.rb:181:5:183:7 | &block [captured self, @x] | captured_variables.rb:187:18:187:19 | self [@x] | +| captured_variables.rb:185:5:189:7 | self in baz [@x] | captured_variables.rb:186:18:188:11 | do ... end [captured self, @x] | +| captured_variables.rb:186:18:188:11 | do ... end [captured self, @x] | captured_variables.rb:181:5:183:7 | &block [captured self, @x] | | captured_variables.rb:187:18:187:19 | self [@x] | captured_variables.rb:187:18:187:19 | @x | | captured_variables.rb:193:1:193:1 | [post] c [@x] | captured_variables.rb:194:1:194:1 | c [@x] | | captured_variables.rb:194:1:194:1 | c [@x] | captured_variables.rb:185:5:189:7 | self in baz [@x] | @@ -188,18 +254,40 @@ edges | instance_variables.rb:121:7:121:24 | call to new | instance_variables.rb:121:1:121:3 | bar | nodes | captured_variables.rb:9:24:9:24 | x | semmle.label | x | +| captured_variables.rb:10:5:10:6 | fn [captured x] | semmle.label | fn [captured x] | +| captured_variables.rb:10:10:10:23 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | | captured_variables.rb:10:20:10:20 | x | semmle.label | x | +| captured_variables.rb:11:5:11:6 | fn [captured x] | semmle.label | fn [captured x] | | captured_variables.rb:13:20:13:29 | call to taint | semmle.label | call to taint | +| captured_variables.rb:15:28:15:28 | x | semmle.label | x | +| captured_variables.rb:16:5:18:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | +| captured_variables.rb:17:14:17:14 | x | semmle.label | x | +| captured_variables.rb:20:1:20:35 | ( ... ) [captured x] | semmle.label | ( ... ) [captured x] | +| captured_variables.rb:20:2:20:34 | call to capture_escape_return1 [captured x] | semmle.label | call to capture_escape_return1 [captured x] | +| captured_variables.rb:20:25:20:34 | call to taint | semmle.label | call to taint | +| captured_variables.rb:22:28:22:28 | x | semmle.label | x | +| captured_variables.rb:23:5:25:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | +| captured_variables.rb:24:14:24:14 | x | semmle.label | x | +| captured_variables.rb:27:25:27:57 | call to capture_escape_return2 [captured x] | semmle.label | call to capture_escape_return2 [captured x] | +| captured_variables.rb:27:48:27:57 | call to taint | semmle.label | call to taint | | captured_variables.rb:29:33:29:33 | x | semmle.label | x | +| captured_variables.rb:30:5:30:6 | fn [captured x] | semmle.label | fn [captured x] | +| captured_variables.rb:30:10:32:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | | captured_variables.rb:31:14:31:14 | x | semmle.label | x | +| captured_variables.rb:33:29:33:30 | fn [captured x] | semmle.label | fn [captured x] | | captured_variables.rb:35:29:35:38 | call to taint | semmle.label | call to taint | +| captured_variables.rb:37:13:37:14 | fn [captured x] | semmle.label | fn [captured x] | +| captured_variables.rb:38:5:38:6 | fn [captured x] | semmle.label | fn [captured x] | | captured_variables.rb:40:31:40:31 | x | semmle.label | x | +| captured_variables.rb:41:5:41:6 | fn [captured x] | semmle.label | fn [captured x] | +| captured_variables.rb:41:10:43:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | | captured_variables.rb:42:14:42:14 | x | semmle.label | x | +| captured_variables.rb:44:13:44:14 | fn [captured x] | semmle.label | fn [captured x] | | captured_variables.rb:46:27:46:36 | call to taint | semmle.label | call to taint | -| captured_variables.rb:48:1:48:1 | x | semmle.label | x | | captured_variables.rb:48:5:48:12 | call to taint | semmle.label | call to taint | +| captured_variables.rb:49:16:52:3 | [post] do ... end [captured x] | semmle.label | [post] do ... end [captured x] | +| captured_variables.rb:49:16:52:3 | do ... end [captured x] | semmle.label | do ... end [captured x] | | captured_variables.rb:50:10:50:10 | x | semmle.label | x | -| captured_variables.rb:51:5:51:5 | x | semmle.label | x | | captured_variables.rb:51:9:51:16 | call to taint | semmle.label | call to taint | | captured_variables.rb:54:6:54:6 | x | semmle.label | x | | captured_variables.rb:57:19:57:19 | x | semmle.label | x | @@ -211,34 +299,68 @@ nodes | captured_variables.rb:61:16:61:21 | self [@field] | semmle.label | self [@field] | | captured_variables.rb:66:1:66:3 | [post] foo [@field] | semmle.label | [post] foo [@field] | | captured_variables.rb:66:15:66:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:67:16:70:3 | [post] do ... end [captured foo, @field] | semmle.label | [post] do ... end [captured foo, @field] | +| captured_variables.rb:67:16:70:3 | do ... end [captured foo, @field] | semmle.label | do ... end [captured foo, @field] | +| captured_variables.rb:68:10:68:12 | foo [@field] | semmle.label | foo [@field] | +| captured_variables.rb:68:10:68:22 | call to get_field | semmle.label | call to get_field | +| captured_variables.rb:69:5:69:7 | [post] foo [@field] | semmle.label | [post] foo [@field] | +| captured_variables.rb:69:19:69:26 | call to taint | semmle.label | call to taint | | captured_variables.rb:72:6:72:8 | foo [@field] | semmle.label | foo [@field] | | captured_variables.rb:72:6:72:18 | call to get_field | semmle.label | call to get_field | -| captured_variables.rb:85:1:85:1 | y | semmle.label | y | +| captured_variables.rb:78:20:80:7 | [post] do ... end [captured foo, @field] | semmle.label | [post] do ... end [captured foo, @field] | +| captured_variables.rb:79:9:79:11 | [post] foo [@field] | semmle.label | [post] foo [@field] | +| captured_variables.rb:79:23:79:30 | call to taint | semmle.label | call to taint | +| captured_variables.rb:83:6:83:8 | foo [@field] | semmle.label | foo [@field] | +| captured_variables.rb:83:6:83:18 | call to get_field | semmle.label | call to get_field | | captured_variables.rb:85:5:85:12 | call to taint | semmle.label | call to taint | +| captured_variables.rb:86:1:86:2 | fn [captured y] | semmle.label | fn [captured y] | +| captured_variables.rb:86:6:89:1 | -> { ... } [captured y] | semmle.label | -> { ... } [captured y] | | captured_variables.rb:87:10:87:10 | y | semmle.label | y | -| captured_variables.rb:88:5:88:5 | y | semmle.label | y | | captured_variables.rb:88:9:88:16 | call to taint | semmle.label | call to taint | +| captured_variables.rb:90:1:90:2 | [post] fn [captured y] | semmle.label | [post] fn [captured y] | +| captured_variables.rb:90:1:90:2 | fn [captured y] | semmle.label | fn [captured y] | | captured_variables.rb:91:6:91:6 | y | semmle.label | y | +| captured_variables.rb:93:17:93:17 | x | semmle.label | x | +| captured_variables.rb:94:5:96:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | +| captured_variables.rb:95:14:95:14 | x | semmle.label | x | +| captured_variables.rb:98:1:98:21 | call to capture_arg [captured x] | semmle.label | call to capture_arg [captured x] | +| captured_variables.rb:98:13:98:20 | call to taint | semmle.label | call to taint | | captured_variables.rb:100:21:100:21 | x | semmle.label | x | | captured_variables.rb:101:11:101:11 | x | semmle.label | x | | captured_variables.rb:104:17:104:24 | call to taint | semmle.label | call to taint | | captured_variables.rb:104:31:104:31 | x | semmle.label | x | | captured_variables.rb:105:10:105:10 | x | semmle.label | x | -| captured_variables.rb:109:5:109:5 | x | semmle.label | x | | captured_variables.rb:109:9:109:17 | call to taint | semmle.label | call to taint | +| captured_variables.rb:110:5:110:10 | middle [captured x] | semmle.label | middle [captured x] | +| captured_variables.rb:110:14:116:5 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | +| captured_variables.rb:111:9:111:13 | inner [captured x] | semmle.label | inner [captured x] | +| captured_variables.rb:111:17:114:9 | -> { ... } [captured x] | semmle.label | -> { ... } [captured x] | | captured_variables.rb:112:18:112:18 | x | semmle.label | x | -| captured_variables.rb:113:13:113:13 | x | semmle.label | x | | captured_variables.rb:113:17:113:25 | call to taint | semmle.label | call to taint | +| captured_variables.rb:115:9:115:13 | [post] inner [captured x] | semmle.label | [post] inner [captured x] | +| captured_variables.rb:115:9:115:13 | inner [captured x] | semmle.label | inner [captured x] | +| captured_variables.rb:117:5:117:10 | [post] middle [captured x] | semmle.label | [post] middle [captured x] | +| captured_variables.rb:117:5:117:10 | middle [captured x] | semmle.label | middle [captured x] | | captured_variables.rb:118:10:118:10 | x | semmle.label | x | +| captured_variables.rb:147:5:147:6 | [post] self [@x] | semmle.label | [post] self [@x] | +| captured_variables.rb:147:10:147:18 | call to taint | semmle.label | call to taint | +| captured_variables.rb:149:5:151:7 | &block [captured self, @x] | semmle.label | &block [captured self, @x] | +| captured_variables.rb:153:14:155:7 | do ... end [captured self, @x] | semmle.label | do ... end [captured self, @x] | +| captured_variables.rb:154:14:154:15 | @x | semmle.label | @x | +| captured_variables.rb:154:14:154:15 | self [@x] | semmle.label | self [@x] | | captured_variables.rb:160:9:160:10 | [post] self [@x] | semmle.label | [post] self [@x] | | captured_variables.rb:160:14:160:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:163:5:165:7 | &block [captured self, @x] | semmle.label | &block [captured self, @x] | | captured_variables.rb:167:5:171:7 | self in baz [@x] | semmle.label | self in baz [@x] | +| captured_variables.rb:168:18:170:11 | do ... end [captured self, @x] | semmle.label | do ... end [captured self, @x] | | captured_variables.rb:169:18:169:19 | @x | semmle.label | @x | | captured_variables.rb:169:18:169:19 | self [@x] | semmle.label | self [@x] | | captured_variables.rb:174:1:174:24 | call to new [@x] | semmle.label | call to new [@x] | | captured_variables.rb:178:9:178:10 | [post] self [@x] | semmle.label | [post] self [@x] | | captured_variables.rb:178:14:178:22 | call to taint | semmle.label | call to taint | +| captured_variables.rb:181:5:183:7 | &block [captured self, @x] | semmle.label | &block [captured self, @x] | | captured_variables.rb:185:5:189:7 | self in baz [@x] | semmle.label | self in baz [@x] | +| captured_variables.rb:186:18:188:11 | do ... end [captured self, @x] | semmle.label | do ... end [captured self, @x] | | captured_variables.rb:187:18:187:19 | @x | semmle.label | @x | | captured_variables.rb:187:18:187:19 | self [@x] | semmle.label | self [@x] | | captured_variables.rb:193:1:193:1 | [post] c [@x] | semmle.label | [post] c [@x] | @@ -348,10 +470,21 @@ nodes | instance_variables.rb:121:7:121:24 | call to new | semmle.label | call to new | | instance_variables.rb:122:6:122:8 | bar | semmle.label | bar | subpaths +| captured_variables.rb:20:25:20:34 | call to taint | captured_variables.rb:15:28:15:28 | x | captured_variables.rb:16:5:18:5 | -> { ... } [captured x] | captured_variables.rb:20:2:20:34 | call to capture_escape_return1 [captured x] | +| captured_variables.rb:27:48:27:57 | call to taint | captured_variables.rb:22:28:22:28 | x | captured_variables.rb:23:5:25:5 | -> { ... } [captured x] | captured_variables.rb:27:25:27:57 | call to capture_escape_return2 [captured x] | | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | captured_variables.rb:66:1:66:3 | [post] foo [@field] | | captured_variables.rb:66:15:66:22 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | captured_variables.rb:66:1:66:3 | [post] foo [@field] | +| captured_variables.rb:68:10:68:12 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | captured_variables.rb:68:10:68:22 | call to get_field | +| captured_variables.rb:68:10:68:12 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | captured_variables.rb:68:10:68:22 | call to get_field | +| captured_variables.rb:69:19:69:26 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | captured_variables.rb:69:5:69:7 | [post] foo [@field] | +| captured_variables.rb:69:19:69:26 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | captured_variables.rb:69:5:69:7 | [post] foo [@field] | | captured_variables.rb:72:6:72:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | captured_variables.rb:72:6:72:18 | call to get_field | | captured_variables.rb:72:6:72:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | captured_variables.rb:72:6:72:18 | call to get_field | +| captured_variables.rb:79:23:79:30 | call to taint | captured_variables.rb:57:19:57:19 | x | captured_variables.rb:58:9:58:14 | [post] self [@field] | captured_variables.rb:79:9:79:11 | [post] foo [@field] | +| captured_variables.rb:79:23:79:30 | call to taint | instance_variables.rb:10:19:10:19 | x | instance_variables.rb:11:9:11:14 | [post] self [@field] | captured_variables.rb:79:9:79:11 | [post] foo [@field] | +| captured_variables.rb:83:6:83:8 | foo [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | captured_variables.rb:83:6:83:18 | call to get_field | +| captured_variables.rb:83:6:83:8 | foo [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | captured_variables.rb:83:6:83:18 | call to get_field | +| captured_variables.rb:98:13:98:20 | call to taint | captured_variables.rb:93:17:93:17 | x | captured_variables.rb:94:5:96:5 | -> { ... } [captured x] | captured_variables.rb:98:1:98:21 | call to capture_arg [captured x] | | instance_variables.rb:28:20:28:24 | field | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:28:9:28:25 | [post] self [@field] | | instance_variables.rb:33:13:33:13 | x | instance_variables.rb:22:20:22:24 | field | instance_variables.rb:23:9:23:14 | [post] self [@field] | instance_variables.rb:33:9:33:14 | call to new [@field] | | instance_variables.rb:36:10:36:23 | call to new [@field] | captured_variables.rb:60:5:62:7 | self in get_field [@field] | captured_variables.rb:61:9:61:21 | return | instance_variables.rb:36:10:36:33 | call to get_field | @@ -416,18 +549,26 @@ subpaths | instance_variables.rb:120:6:120:10 | foo16 [@field] | instance_variables.rb:13:5:15:7 | self in get_field [@field] | instance_variables.rb:14:9:14:21 | return | instance_variables.rb:120:6:120:20 | call to get_field | #select | captured_variables.rb:10:20:10:20 | x | captured_variables.rb:13:20:13:29 | call to taint | captured_variables.rb:10:20:10:20 | x | $@ | captured_variables.rb:13:20:13:29 | call to taint | call to taint | +| captured_variables.rb:17:14:17:14 | x | captured_variables.rb:20:25:20:34 | call to taint | captured_variables.rb:17:14:17:14 | x | $@ | captured_variables.rb:20:25:20:34 | call to taint | call to taint | +| captured_variables.rb:24:14:24:14 | x | captured_variables.rb:27:48:27:57 | call to taint | captured_variables.rb:24:14:24:14 | x | $@ | captured_variables.rb:27:48:27:57 | call to taint | call to taint | | captured_variables.rb:31:14:31:14 | x | captured_variables.rb:35:29:35:38 | call to taint | captured_variables.rb:31:14:31:14 | x | $@ | captured_variables.rb:35:29:35:38 | call to taint | call to taint | | captured_variables.rb:42:14:42:14 | x | captured_variables.rb:46:27:46:36 | call to taint | captured_variables.rb:42:14:42:14 | x | $@ | captured_variables.rb:46:27:46:36 | call to taint | call to taint | | captured_variables.rb:50:10:50:10 | x | captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:50:10:50:10 | x | $@ | captured_variables.rb:48:5:48:12 | call to taint | call to taint | +| captured_variables.rb:54:6:54:6 | x | captured_variables.rb:48:5:48:12 | call to taint | captured_variables.rb:54:6:54:6 | x | $@ | captured_variables.rb:48:5:48:12 | call to taint | call to taint | | captured_variables.rb:54:6:54:6 | x | captured_variables.rb:51:9:51:16 | call to taint | captured_variables.rb:54:6:54:6 | x | $@ | captured_variables.rb:51:9:51:16 | call to taint | call to taint | +| captured_variables.rb:68:10:68:22 | call to get_field | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:68:10:68:22 | call to get_field | $@ | captured_variables.rb:66:15:66:22 | call to taint | call to taint | | captured_variables.rb:72:6:72:18 | call to get_field | captured_variables.rb:66:15:66:22 | call to taint | captured_variables.rb:72:6:72:18 | call to get_field | $@ | captured_variables.rb:66:15:66:22 | call to taint | call to taint | +| captured_variables.rb:72:6:72:18 | call to get_field | captured_variables.rb:69:19:69:26 | call to taint | captured_variables.rb:72:6:72:18 | call to get_field | $@ | captured_variables.rb:69:19:69:26 | call to taint | call to taint | +| captured_variables.rb:83:6:83:18 | call to get_field | captured_variables.rb:79:23:79:30 | call to taint | captured_variables.rb:83:6:83:18 | call to get_field | $@ | captured_variables.rb:79:23:79:30 | call to taint | call to taint | | captured_variables.rb:87:10:87:10 | y | captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:87:10:87:10 | y | $@ | captured_variables.rb:85:5:85:12 | call to taint | call to taint | -| captured_variables.rb:87:10:87:10 | y | captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:87:10:87:10 | y | $@ | captured_variables.rb:88:9:88:16 | call to taint | call to taint | +| captured_variables.rb:91:6:91:6 | y | captured_variables.rb:85:5:85:12 | call to taint | captured_variables.rb:91:6:91:6 | y | $@ | captured_variables.rb:85:5:85:12 | call to taint | call to taint | | captured_variables.rb:91:6:91:6 | y | captured_variables.rb:88:9:88:16 | call to taint | captured_variables.rb:91:6:91:6 | y | $@ | captured_variables.rb:88:9:88:16 | call to taint | call to taint | +| captured_variables.rb:95:14:95:14 | x | captured_variables.rb:98:13:98:20 | call to taint | captured_variables.rb:95:14:95:14 | x | $@ | captured_variables.rb:98:13:98:20 | call to taint | call to taint | | captured_variables.rb:105:10:105:10 | x | captured_variables.rb:104:17:104:24 | call to taint | captured_variables.rb:105:10:105:10 | x | $@ | captured_variables.rb:104:17:104:24 | call to taint | call to taint | | captured_variables.rb:112:18:112:18 | x | captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:112:18:112:18 | x | $@ | captured_variables.rb:109:9:109:17 | call to taint | call to taint | -| captured_variables.rb:112:18:112:18 | x | captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:112:18:112:18 | x | $@ | captured_variables.rb:113:17:113:25 | call to taint | call to taint | +| captured_variables.rb:118:10:118:10 | x | captured_variables.rb:109:9:109:17 | call to taint | captured_variables.rb:118:10:118:10 | x | $@ | captured_variables.rb:109:9:109:17 | call to taint | call to taint | | captured_variables.rb:118:10:118:10 | x | captured_variables.rb:113:17:113:25 | call to taint | captured_variables.rb:118:10:118:10 | x | $@ | captured_variables.rb:113:17:113:25 | call to taint | call to taint | +| captured_variables.rb:154:14:154:15 | @x | captured_variables.rb:147:10:147:18 | call to taint | captured_variables.rb:154:14:154:15 | @x | $@ | captured_variables.rb:147:10:147:18 | call to taint | call to taint | | captured_variables.rb:169:18:169:19 | @x | captured_variables.rb:160:14:160:22 | call to taint | captured_variables.rb:169:18:169:19 | @x | $@ | captured_variables.rb:160:14:160:22 | call to taint | call to taint | | captured_variables.rb:187:18:187:19 | @x | captured_variables.rb:178:14:178:22 | call to taint | captured_variables.rb:187:18:187:19 | @x | $@ | captured_variables.rb:178:14:178:22 | 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 | diff --git a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected index 48fed82c4e95..204399860fdf 100644 --- a/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected +++ b/ruby/ql/test/library-tests/dataflow/global/TypeTrackingInlineTest.expected @@ -1,13 +1,13 @@ testFailures -| captured_variables.rb:17:14:17:14 | x | Fixed missing result:hasValueFlow=1.2 | -| captured_variables.rb:24:14:24:14 | x | Fixed missing result:hasValueFlow=1.3 | | captured_variables.rb:50:10:50:10 | x | Fixed missing result:hasValueFlow=2 | -| captured_variables.rb:54:6:54:6 | x | Unexpected result: hasValueFlow=1 | -| captured_variables.rb:72:21:72:75 | # $ MISSING: hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 | Fixed spurious result:hasValueFlow=3 | -| captured_variables.rb:91:6:91:6 | y | Unexpected result: hasValueFlow=6 | -| captured_variables.rb:95:14:95:14 | x | Fixed missing result:hasValueFlow=8 | -| captured_variables.rb:118:10:118:10 | x | Unexpected result: hasValueFlow=10 | +| captured_variables.rb:68:25:68:68 | # $ hasValueFlow=3 $ MISSING: hasValueFlow=4 | Missing result:hasValueFlow=3 | +| captured_variables.rb:72:21:72:66 | # $ hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 | Fixed spurious result:hasValueFlow=3 | +| captured_variables.rb:72:21:72:66 | # $ hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 | Missing result:hasValueFlow=4 | +| captured_variables.rb:83:21:83:38 | # $ hasValueFlow=5 | Missing result:hasValueFlow=5 | +| captured_variables.rb:87:10:87:10 | y | Unexpected result: hasValueFlow=7 | +| captured_variables.rb:112:18:112:18 | x | Unexpected result: hasValueFlow=11 | | captured_variables.rb:126:14:126:14 | x | Fixed missing result:hasValueFlow=12 | +| captured_variables.rb:154:17:154:35 | # $ hasValueFlow=13 | Missing result:hasValueFlow=13 | | instance_variables.rb:20:16:20:33 | # $ hasValueFlow=7 | Missing result:hasValueFlow=7 | | instance_variables.rb:36:36:36:54 | # $ hasValueFlow=34 | Missing result:hasValueFlow=34 | | instance_variables.rb:39:36:39:54 | # $ hasValueFlow=35 | Missing result:hasValueFlow=35 | diff --git a/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb index 2a165b3b3c16..e0ecc1da2c5f 100644 --- a/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb +++ b/ruby/ql/test/library-tests/dataflow/global/captured_variables.rb @@ -14,14 +14,14 @@ def capture_local_call x def capture_escape_return1 x -> { - sink(x) # $ MISSING: hasValueFlow=1.2 + sink(x) # $ hasValueFlow=1.2 } end (capture_escape_return1 taint(1.2)).call def capture_escape_return2 x -> { - sink(x) # $ MISSING: hasValueFlow=1.3 + sink(x) # $ hasValueFlow=1.3 } end Something.unknownMethod(capture_escape_return2 taint(1.3)) @@ -51,7 +51,7 @@ def capture_escape_known_call x x = taint(2) end -sink x # $ hasValueFlow=2 +sink x # $ hasValueFlow=2 $ SPURIOUS: hasValueFlow=1 class Foo def set_field x @@ -65,11 +65,11 @@ def get_field foo = Foo.new foo.set_field(taint(3)) [1, 2, 3].each do |i| - sink(foo.get_field) # $ MISSING: hasValueFlow=3 $ MISSING: hasValueFlow=4 + sink(foo.get_field) # $ hasValueFlow=3 $ MISSING: hasValueFlow=4 foo.set_field(taint(4)) end -sink(foo.get_field) # $ MISSING: hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 +sink(foo.get_field) # $ hasValueFlow=4 $ SPURIOUS: hasValueFlow=3 foo = Foo.new if (rand() < 0) then @@ -80,19 +80,19 @@ def get_field end end -sink(foo.get_field) # $ MISSING: hasValueFlow=5 +sink(foo.get_field) # $ hasValueFlow=5 y = taint(6) fn = -> { - sink(y) # $ hasValueFlow=6 $ SPURIOUS: hasValueFlow=7 + sink(y) # $ hasValueFlow=6 y = taint(7) } fn.call -sink(y) # $ hasValueFlow=7 +sink(y) # $ hasValueFlow=7 $ SPURIOUS: hasValueFlow=6 def capture_arg x -> { - sink x # $ MISSING: hasValueFlow=8 + sink x # $ hasValueFlow=8 } end capture_arg(taint(8)).call @@ -109,13 +109,13 @@ def capture_nested x = taint(10) middle = -> { inner = -> { - sink x # $ hasValueFlow=10 $ SPURIOUS: hasValueFlow=11 + sink x # $ hasValueFlow=10 x = taint(11) } inner.call } middle.call - sink x # $ hasValueFlow=11 + sink x # $ hasValueFlow=11 $ SPURIOUS: hasValueFlow=10 end capture_nested @@ -151,7 +151,7 @@ def self.foo end self.foo do - sink @x # $ MISSING: hasValueFlow=13 + sink @x # $ hasValueFlow=13 end end diff --git a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected index a9c22a3733cf..bbfebfb4233a 100644 --- a/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected +++ b/ruby/ql/test/library-tests/dataflow/summaries/Summaries.expected @@ -1,59 +1,57 @@ testFailures edges -| summaries.rb:1:1:1:7 | tainted | summaries.rb:2:6:2:12 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:2:6:2:12 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:4:24:4:30 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:4:24:4:30 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:16:36:16:42 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:16:36:16:42 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:20:25:20:31 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:26:31:26:37 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:30:24:30:30 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:31:27:31:33 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:34:16:34:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:34:16:34:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:35:16:35:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:35:16:35:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:36:21:36:27 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:36:21:36:27 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:37:36:37:42 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:37:36:37:42 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:51:24:51:30 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:56:22:56:28 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:57:17:57:23 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:59:27:59:33 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:63:32:63:38 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:65:23:65:29 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:122:16:122:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:128:14:128:20 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:131:16:131:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:131:16:131:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:132:21:132:27 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:132:21:132:27 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:135:26:135:32 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:135:26:135:32 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:137:23:137:29 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:137:23:137:29 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:140:19:140:25 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:140:19:140:25 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:141:19:141:25 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:141:19:141:25 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:145:26:145:32 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:145:26:145:32 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:147:16:147:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:147:16:147:22 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:150:39:150:45 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:150:39:150:45 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:154:20:154:26 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:154:20:154:26 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:155:28:155:34 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:155:28:155:34 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:156:27:156:33 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:156:27:156:33 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:158:15:158:21 | tainted | -| summaries.rb:1:1:1:7 | tainted | summaries.rb:158:15:158:21 | tainted | -| summaries.rb:1:11:1:36 | call to identity | summaries.rb:1:1:1:7 | tainted | -| summaries.rb:1:11:1:36 | call to identity | summaries.rb:1:1:1:7 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:2:6:2:12 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:2:6:2:12 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:4:24:4:30 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:4:24:4:30 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:16:36:16:42 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:16:36:16:42 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:20:25:20:31 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:26:31:26:37 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:30:24:30:30 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:31:27:31:33 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:34:16:34:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:35:16:35:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:36:21:36:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:37:36:37:42 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:51:24:51:30 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:56:22:56:28 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:57:17:57:23 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:59:27:59:33 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:63:32:63:38 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:65:23:65:29 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:122:16:122:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:128:14:128:20 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:131:16:131:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:132:21:132:27 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:135:26:135:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:137:23:137:29 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:140:19:140:25 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:141:19:141:25 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:145:26:145:32 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:147:16:147:22 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:150:39:150:45 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:154:20:154:26 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:155:28:155:34 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:156:27:156:33 | tainted | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:157:14:160:3 | do ... end [captured tainted] | +| summaries.rb:1:11:1:36 | call to identity | summaries.rb:157:14:160:3 | do ... end [captured tainted] | | summaries.rb:1:20:1:36 | call to source | summaries.rb:1:11:1:36 | call to identity | | summaries.rb:1:20:1:36 | call to source | summaries.rb:1:11:1:36 | call to identity | | summaries.rb:4:1:4:8 | tainted2 | summaries.rb:9:6:9:13 | tainted2 | @@ -242,6 +240,7 @@ edges | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:154:20:154:26 | tainted | | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:155:28:155:34 | tainted | | summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:156:27:156:33 | tainted | +| summaries.rb:122:16:122:22 | [post] tainted | summaries.rb:157:14:160:3 | do ... end [captured tainted] | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:16:122:22 | [post] tainted | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:25:122:25 | [post] y | | summaries.rb:122:16:122:22 | tainted | summaries.rb:122:33:122:33 | [post] z | @@ -249,9 +248,9 @@ edges | summaries.rb:122:33:122:33 | [post] z | summaries.rb:125:6:125:6 | z | | summaries.rb:128:1:128:1 | [post] x | summaries.rb:129:6:129:6 | x | | summaries.rb:128:14:128:20 | tainted | summaries.rb:128:1:128:1 | [post] x | +| summaries.rb:157:14:160:3 | do ... end [captured tainted] | summaries.rb:158:15:158:21 | tainted | +| summaries.rb:157:14:160:3 | do ... end [captured tainted] | summaries.rb:158:15:158:21 | tainted | nodes -| summaries.rb:1:1:1:7 | tainted | semmle.label | tainted | -| summaries.rb:1:1:1:7 | tainted | semmle.label | tainted | | summaries.rb:1:11:1:36 | call to identity | semmle.label | call to identity | | summaries.rb:1:11:1:36 | call to identity | semmle.label | call to identity | | summaries.rb:1:20:1:36 | call to source | semmle.label | call to source | @@ -491,6 +490,8 @@ nodes | summaries.rb:155:28:155:34 | tainted | semmle.label | tainted | | summaries.rb:156:27:156:33 | tainted | semmle.label | tainted | | summaries.rb:156:27:156:33 | tainted | semmle.label | tainted | +| summaries.rb:157:14:160:3 | do ... end [captured tainted] | semmle.label | do ... end [captured tainted] | +| summaries.rb:157:14:160:3 | do ... end [captured tainted] | semmle.label | do ... end [captured tainted] | | summaries.rb:158:15:158:21 | tainted | semmle.label | tainted | | summaries.rb:158:15:158:21 | tainted | semmle.label | tainted | | summaries.rb:163:20:163:36 | call to source | semmle.label | call to source | diff --git a/ruby/ql/test/library-tests/frameworks/sinatra/Sinatra.expected b/ruby/ql/test/library-tests/frameworks/sinatra/Sinatra.expected index 1c2437eb216c..33495e743fd1 100644 --- a/ruby/ql/test/library-tests/frameworks/sinatra/Sinatra.expected +++ b/ruby/ql/test/library-tests/frameworks/sinatra/Sinatra.expected @@ -43,51 +43,43 @@ filterPatterns | app.rb:106:3:108:5 | call to before | app.rb:106:10:106:23 | "/protected/*" | | app.rb:111:3:113:5 | call to after | app.rb:111:9:111:23 | "/create/:slug" | additionalFlowSteps -| app.rb:85:5:85:9 | [post] self | app.rb:2:22:4:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:10:21:13:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:15:23:18:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:24:26:26:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:37:16:42:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:44:53:46:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:56:32:58:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:60:48:62:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:74:11:77:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:79:11:82:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:89:16:92:5 | self | -| app.rb:85:5:85:9 | [post] self | app.rb:94:15:96:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:2:22:4:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:10:21:13:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:15:23:18:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:24:26:26:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:37:16:42:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:44:53:46:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:56:32:58:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:60:48:62:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:74:11:77:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:79:11:82:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:89:16:92:5 | self | -| app.rb:86:5:86:11 | [post] self | app.rb:94:15:96:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:2:22:4:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:10:21:13:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:15:23:18:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:24:26:26:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:37:16:42:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:44:53:46:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:56:32:58:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:60:48:62:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:74:11:77:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:79:11:82:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:89:16:92:5 | self | -| app.rb:103:5:103:9 | [post] self | app.rb:94:15:96:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:2:22:4:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:10:21:13:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:15:23:18:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:24:26:26:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:37:16:42:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:44:53:46:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:56:32:58:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:60:48:62:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:74:11:77:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:79:11:82:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:89:16:92:5 | self | -| app.rb:103:13:103:22 | [post] self | app.rb:94:15:96:5 | self | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:2:22:4:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:6:24:8:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:10:21:13:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:15:23:18:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:20:23:22:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:24:26:26:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:28:26:31:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:33:25:35:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:37:16:42:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:44:53:46:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:48:14:50:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:52:37:54:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:56:32:58:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:60:48:62:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:66:41:68:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:70:20:72:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:74:11:77:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:79:11:82:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:89:16:92:5 | lambda self in do ... end | +| app.rb:84:10:87:5 | [post] do ... end | app.rb:94:15:96:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:2:22:4:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:6:24:8:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:10:21:13:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:15:23:18:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:20:23:22:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:24:26:26:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:28:26:31:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:33:25:35:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:37:16:42:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:44:53:46:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:48:14:50:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:52:37:54:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:56:32:58:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:60:48:62:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:66:41:68:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:70:20:72:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:74:11:77:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:79:11:82:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:89:16:92:5 | lambda self in do ... end | +| app.rb:102:10:104:5 | [post] do ... end | app.rb:94:15:96:5 | lambda self in do ... end | diff --git a/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.expected b/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.expected index f92dd2c22335..103dc82bda4f 100644 --- a/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.expected +++ b/ruby/ql/test/query-tests/experimental/TemplateInjection/TemplateInjection.expected @@ -7,15 +7,15 @@ edges | ErbInjection.rb:8:5:8:12 | bad_text | ErbInjection.rb:19:20:19:27 | bad_text | | ErbInjection.rb:8:16:11:14 | ... % ... | ErbInjection.rb:8:5:8:12 | bad_text | | ErbInjection.rb:11:11:11:14 | name | ErbInjection.rb:8:16:11:14 | ... % ... | -| SlimInjection.rb:5:5:5:8 | name | SlimInjection.rb:8:5:8:12 | bad_text | | SlimInjection.rb:5:5:5:8 | name | SlimInjection.rb:11:11:11:14 | name | -| SlimInjection.rb:5:5:5:8 | name | SlimInjection.rb:17:5:17:13 | bad2_text | +| SlimInjection.rb:5:5:5:8 | name | SlimInjection.rb:14:23:14:34 | { ... } [captured bad_text] | +| SlimInjection.rb:5:5:5:8 | name | SlimInjection.rb:23:23:23:35 | { ... } [captured bad2_text] | | SlimInjection.rb:5:12:5:17 | call to params | SlimInjection.rb:5:12:5:24 | ...[...] | | SlimInjection.rb:5:12:5:24 | ...[...] | SlimInjection.rb:5:5:5:8 | name | -| SlimInjection.rb:8:5:8:12 | bad_text | SlimInjection.rb:14:25:14:32 | bad_text | -| SlimInjection.rb:8:16:11:14 | ... % ... | SlimInjection.rb:8:5:8:12 | bad_text | +| SlimInjection.rb:8:16:11:14 | ... % ... | SlimInjection.rb:14:23:14:34 | { ... } [captured bad_text] | | SlimInjection.rb:11:11:11:14 | name | SlimInjection.rb:8:16:11:14 | ... % ... | -| SlimInjection.rb:17:5:17:13 | bad2_text | SlimInjection.rb:23:25:23:33 | bad2_text | +| SlimInjection.rb:14:23:14:34 | { ... } [captured bad_text] | SlimInjection.rb:14:25:14:32 | bad_text | +| SlimInjection.rb:23:23:23:35 | { ... } [captured bad2_text] | SlimInjection.rb:23:25:23:33 | bad2_text | nodes | ErbInjection.rb:5:5:5:8 | name | semmle.label | name | | ErbInjection.rb:5:12:5:17 | call to params | semmle.label | call to params | @@ -28,11 +28,11 @@ nodes | SlimInjection.rb:5:5:5:8 | name | semmle.label | name | | SlimInjection.rb:5:12:5:17 | call to params | semmle.label | call to params | | SlimInjection.rb:5:12:5:24 | ...[...] | semmle.label | ...[...] | -| SlimInjection.rb:8:5:8:12 | bad_text | semmle.label | bad_text | | SlimInjection.rb:8:16:11:14 | ... % ... | semmle.label | ... % ... | | SlimInjection.rb:11:11:11:14 | name | semmle.label | name | +| SlimInjection.rb:14:23:14:34 | { ... } [captured bad_text] | semmle.label | { ... } [captured bad_text] | | SlimInjection.rb:14:25:14:32 | bad_text | semmle.label | bad_text | -| SlimInjection.rb:17:5:17:13 | bad2_text | semmle.label | bad2_text | +| SlimInjection.rb:23:23:23:35 | { ... } [captured bad2_text] | semmle.label | { ... } [captured bad2_text] | | SlimInjection.rb:23:25:23:33 | bad2_text | semmle.label | bad2_text | subpaths #select diff --git a/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected index 7ee24c4112f1..a93fdd9f008d 100644 --- a/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-022/PathInjection.expected @@ -5,7 +5,9 @@ edges | ArchiveApiPathTraversal.rb:10:11:10:23 | ...[...] | ArchiveApiPathTraversal.rb:67:13:67:16 | file | | ArchiveApiPathTraversal.rb:15:9:15:14 | call to params | ArchiveApiPathTraversal.rb:15:9:15:25 | ...[...] | | ArchiveApiPathTraversal.rb:15:9:15:25 | ...[...] | ArchiveApiPathTraversal.rb:75:11:75:18 | filename | -| ArchiveApiPathTraversal.rb:49:17:49:27 | destination | ArchiveApiPathTraversal.rb:52:38:52:48 | destination | +| ArchiveApiPathTraversal.rb:49:17:49:27 | destination | ArchiveApiPathTraversal.rb:50:36:64:7 | do ... end [captured destination] | +| ArchiveApiPathTraversal.rb:50:36:64:7 | do ... end [captured destination] | ArchiveApiPathTraversal.rb:51:16:63:9 | do ... end [captured destination] | +| ArchiveApiPathTraversal.rb:51:16:63:9 | do ... end [captured destination] | ArchiveApiPathTraversal.rb:52:38:52:48 | destination | | ArchiveApiPathTraversal.rb:52:9:52:24 | destination_file | ArchiveApiPathTraversal.rb:59:21:59:36 | destination_file | | ArchiveApiPathTraversal.rb:52:28:52:67 | call to join | ArchiveApiPathTraversal.rb:52:9:52:24 | destination_file | | ArchiveApiPathTraversal.rb:52:38:52:48 | destination | ArchiveApiPathTraversal.rb:52:28:52:67 | call to join | @@ -72,6 +74,8 @@ nodes | ArchiveApiPathTraversal.rb:15:9:15:14 | call to params | semmle.label | call to params | | ArchiveApiPathTraversal.rb:15:9:15:25 | ...[...] | semmle.label | ...[...] | | ArchiveApiPathTraversal.rb:49:17:49:27 | destination | semmle.label | destination | +| ArchiveApiPathTraversal.rb:50:36:64:7 | do ... end [captured destination] | semmle.label | do ... end [captured destination] | +| ArchiveApiPathTraversal.rb:51:16:63:9 | do ... end [captured destination] | semmle.label | do ... end [captured destination] | | ArchiveApiPathTraversal.rb:52:9:52:24 | destination_file | semmle.label | destination_file | | ArchiveApiPathTraversal.rb:52:28:52:67 | call to join | semmle.label | call to join | | ArchiveApiPathTraversal.rb:52:38:52:48 | destination | semmle.label | destination | diff --git a/ruby/ql/test/query-tests/security/cwe-117/LogInjection.expected b/ruby/ql/test/query-tests/security/cwe-117/LogInjection.expected index bb29e10f3f21..c2cbd5dd2ed7 100644 --- a/ruby/ql/test/query-tests/security/cwe-117/LogInjection.expected +++ b/ruby/ql/test/query-tests/security/cwe-117/LogInjection.expected @@ -4,14 +4,16 @@ edges | app/controllers/users_controller.rb:15:5:15:15 | unsanitized | app/controllers/users_controller.rb:23:20:23:30 | unsanitized | | app/controllers/users_controller.rb:15:19:15:24 | call to params | app/controllers/users_controller.rb:15:19:15:30 | ...[...] | | app/controllers/users_controller.rb:15:19:15:30 | ...[...] | app/controllers/users_controller.rb:15:5:15:15 | unsanitized | -| app/controllers/users_controller.rb:23:5:23:16 | unsanitized2 | app/controllers/users_controller.rb:25:7:25:18 | unsanitized2 | -| app/controllers/users_controller.rb:23:5:23:16 | unsanitized2 | app/controllers/users_controller.rb:27:16:27:39 | ... + ... | | app/controllers/users_controller.rb:23:20:23:30 | unsanitized | app/controllers/users_controller.rb:23:20:23:44 | call to sub | -| app/controllers/users_controller.rb:23:20:23:44 | call to sub | app/controllers/users_controller.rb:23:5:23:16 | unsanitized2 | -| app/controllers/users_controller.rb:33:5:33:15 | unsanitized | app/controllers/users_controller.rb:34:33:34:43 | unsanitized | -| app/controllers/users_controller.rb:33:5:33:15 | unsanitized | app/controllers/users_controller.rb:35:33:35:55 | ... + ... | +| app/controllers/users_controller.rb:23:20:23:44 | call to sub | app/controllers/users_controller.rb:24:18:26:7 | do ... end [captured unsanitized2] | +| app/controllers/users_controller.rb:23:20:23:44 | call to sub | app/controllers/users_controller.rb:27:16:27:39 | ... + ... | +| app/controllers/users_controller.rb:24:18:26:7 | do ... end [captured unsanitized2] | app/controllers/users_controller.rb:25:7:25:18 | unsanitized2 | | app/controllers/users_controller.rb:33:19:33:25 | call to cookies | app/controllers/users_controller.rb:33:19:33:31 | ...[...] | -| app/controllers/users_controller.rb:33:19:33:31 | ...[...] | app/controllers/users_controller.rb:33:5:33:15 | unsanitized | +| app/controllers/users_controller.rb:33:19:33:31 | ...[...] | app/controllers/users_controller.rb:34:31:34:45 | { ... } [captured unsanitized] | +| app/controllers/users_controller.rb:33:19:33:31 | ...[...] | app/controllers/users_controller.rb:35:31:35:57 | { ... } [captured unsanitized] | +| app/controllers/users_controller.rb:34:31:34:45 | { ... } [captured unsanitized] | app/controllers/users_controller.rb:34:33:34:43 | unsanitized | +| app/controllers/users_controller.rb:35:31:35:57 | { ... } [captured unsanitized] | app/controllers/users_controller.rb:35:45:35:55 | unsanitized | +| app/controllers/users_controller.rb:35:45:35:55 | unsanitized | app/controllers/users_controller.rb:35:33:35:55 | ... + ... | | app/controllers/users_controller.rb:49:19:49:24 | call to params | app/controllers/users_controller.rb:49:19:49:30 | ...[...] | nodes | app/controllers/users_controller.rb:15:5:15:15 | unsanitized | semmle.label | unsanitized | @@ -19,16 +21,18 @@ nodes | app/controllers/users_controller.rb:15:19:15:30 | ...[...] | semmle.label | ...[...] | | app/controllers/users_controller.rb:16:19:16:29 | unsanitized | semmle.label | unsanitized | | app/controllers/users_controller.rb:17:19:17:41 | ... + ... | semmle.label | ... + ... | -| app/controllers/users_controller.rb:23:5:23:16 | unsanitized2 | semmle.label | unsanitized2 | | app/controllers/users_controller.rb:23:20:23:30 | unsanitized | semmle.label | unsanitized | | app/controllers/users_controller.rb:23:20:23:44 | call to sub | semmle.label | call to sub | +| app/controllers/users_controller.rb:24:18:26:7 | do ... end [captured unsanitized2] | semmle.label | do ... end [captured unsanitized2] | | app/controllers/users_controller.rb:25:7:25:18 | unsanitized2 | semmle.label | unsanitized2 | | app/controllers/users_controller.rb:27:16:27:39 | ... + ... | semmle.label | ... + ... | -| app/controllers/users_controller.rb:33:5:33:15 | unsanitized | semmle.label | unsanitized | | app/controllers/users_controller.rb:33:19:33:25 | call to cookies | semmle.label | call to cookies | | app/controllers/users_controller.rb:33:19:33:31 | ...[...] | semmle.label | ...[...] | +| app/controllers/users_controller.rb:34:31:34:45 | { ... } [captured unsanitized] | semmle.label | { ... } [captured unsanitized] | | app/controllers/users_controller.rb:34:33:34:43 | unsanitized | semmle.label | unsanitized | +| app/controllers/users_controller.rb:35:31:35:57 | { ... } [captured unsanitized] | semmle.label | { ... } [captured unsanitized] | | app/controllers/users_controller.rb:35:33:35:55 | ... + ... | semmle.label | ... + ... | +| app/controllers/users_controller.rb:35:45:35:55 | unsanitized | semmle.label | unsanitized | | app/controllers/users_controller.rb:49:19:49:24 | call to params | semmle.label | call to params | | app/controllers/users_controller.rb:49:19:49:30 | ...[...] | semmle.label | ...[...] | subpaths From 6de315d086595d666fd64d0afe27b4f41ebde232 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 17 Aug 2023 09:59:43 +0200 Subject: [PATCH 3/5] Add change note --- ruby/ql/lib/change-notes/2023-08-23-variable-capture-flow.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 ruby/ql/lib/change-notes/2023-08-23-variable-capture-flow.md diff --git a/ruby/ql/lib/change-notes/2023-08-23-variable-capture-flow.md b/ruby/ql/lib/change-notes/2023-08-23-variable-capture-flow.md new file mode 100644 index 000000000000..66ab65083dc3 --- /dev/null +++ b/ruby/ql/lib/change-notes/2023-08-23-variable-capture-flow.md @@ -0,0 +1,4 @@ +--- +category: majorAnalysis +--- +* Improved support for flow through captured variables that properly adheres to inter-procedural control flow. \ No newline at end of file From a06a9ffa29c7aa935c0015947c335b9230b56342 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Thu, 31 Aug 2023 08:57:41 +0200 Subject: [PATCH 4/5] Address review comments --- .../lib/codeql/ruby/controlflow/CfgNodes.qll | 6 ++- .../lib/codeql/ruby/dataflow/FlowSummary.qll | 41 ++++++++----------- .../ruby/dataflow/internal/DataFlowPublic.qll | 6 ++- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll index e37493910b2c..c8b369464b95 100644 --- a/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll +++ b/ruby/ql/lib/codeql/ruby/controlflow/CfgNodes.qll @@ -201,7 +201,11 @@ module ExprNodes { override LhsExpr getExpr() { result = super.getExpr() } - /** Gets a variable used in (or introduced by) this LHS. */ + /** + * DEPRECATED: use `getVariable` instead. + * + * Gets a variable used in (or introduced by) this LHS. + */ deprecated Variable getAVariable() { result = e.(VariableAccess).getVariable() } /** Gets the variable used in (or introduced by) this LHS. */ diff --git a/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll index 6a788f8d75c7..7e7004c6e619 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/FlowSummary.qll @@ -163,7 +163,7 @@ class RequiredSummaryComponentStack = Impl::Public::RequiredSummaryComponentStac /** * Provides a set of special flow summaries to ensure that callbacks passed into - * library methods will be passed as `self` arguments into themeselves. That is, + * library methods will be passed as `lambda-self` arguments into themselves. That is, * we are assuming that callbacks passed into library methods will be called, which is * needed for flow through captured variables. */ @@ -172,21 +172,6 @@ private module LibraryCallbackSummaries { not exists(getTarget(call)) } - private class LibraryBlockMethod extends SummarizedCallable { - LibraryBlockMethod() { this = "" } - - final override MethodCall getACall() { - libraryCall(result.getAControlFlowNode()) and - result.hasBlock() - } - - override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[block]" and - output = "Argument[block].Parameter[lambda-self]" and - preservesValue = true - } - } - private DataFlow::LocalSourceNode trackLambdaCreation(TypeTracker t) { t.start() and lambdaCreation(result, TLambdaCallKind(), _) @@ -205,20 +190,26 @@ private module LibraryCallbackSummaries { } private class LibraryLambdaMethod extends SummarizedCallable { - private int i; - - LibraryLambdaMethod() { - this = "" and - i in [0 .. 10] - } + LibraryLambdaMethod() { this = "" } final override MethodCall getACall() { - libraryCallHasLambdaArg(result.getAControlFlowNode(), i) + libraryCall(result.getAControlFlowNode()) and + result.hasBlock() + or + libraryCallHasLambdaArg(result.getAControlFlowNode(), _) } override predicate propagatesFlowExt(string input, string output, boolean preservesValue) { - input = "Argument[" + i + "]" and - output = "Argument[" + i + "].Parameter[lambda-self]" and + ( + input = "Argument[block]" and + output = "Argument[block].Parameter[lambda-self]" + or + exists(int i | + i in [0 .. 10] and + input = "Argument[" + i + "]" and + output = "Argument[" + i + "].Parameter[lambda-self]" + ) + ) and preservesValue = true } } diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll index 6548c4ebe477..99c9549b72f9 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPublic.qll @@ -1219,7 +1219,11 @@ class LhsExprNode extends ExprNode { /** Gets the underlying AST node as a `LhsExpr`. */ LhsExpr asLhsExprAstNode() { result = lhsExprCfgNode.getExpr() } - /** Gets a variable used in (or introduced by) this LHS. */ + /** + * DEPRECATED: use `getVariable` instead. + * + * Gets a variable used in (or introduced by) this LHS. + */ deprecated Variable getAVariable() { result = lhsExprCfgNode.getAVariable() } /** Gets the variable used in (or introduced by) this LHS. */ From 88d2e2590f6db462455e825f592b397efcfd9d68 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Wed, 13 Sep 2023 08:52:22 +0200 Subject: [PATCH 5/5] Ruby: Rename `LambdaSelfParameterNode` to `LambdaSelfReferenceNode` --- .../dataflow/internal/DataFlowImplSpecific.qll | 2 +- .../ruby/dataflow/internal/DataFlowPrivate.qll | 16 ++++++++-------- ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll index abe0366c513f..273fa395ea66 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowImplSpecific.qll @@ -17,7 +17,7 @@ module RubyDataFlow implements InputSig { import Private import Public - // includes `LambdaSelfParameterNode`, which is not part of the public API + // includes `LambdaSelfReferenceNode`, which is not part of the public API class ParameterNode = Private::ParameterNodeImpl; predicate isParameterNode(ParameterNode p, DataFlowCallable c, ParameterPosition pos) { diff --git a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll index fa1467aa25c2..010067604e93 100644 --- a/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll +++ b/ruby/ql/lib/codeql/ruby/dataflow/internal/DataFlowPrivate.qll @@ -375,7 +375,7 @@ module VariableCapture { or result.(Flow::ParameterNode).getParameter().getParameterNode() = n or - result.(Flow::ThisParameterNode).getCallable() = n.(LambdaSelfParameterNode).getCallable() + result.(Flow::ThisParameterNode).getCallable() = n.(LambdaSelfReferenceNode).getCallable() } predicate storeStep(Node node1, Content::CapturedVariableContent c, Node node2) { @@ -443,7 +443,7 @@ private module Cached { p instanceof SplatParameter } or TSelfParameterNode(MethodBase m) or - TLambdaSelfParameterNode(Callable c) { lambdaCreationExpr(_, _, c) } or + TLambdaSelfReferenceNode(Callable c) { lambdaCreationExpr(_, _, c) } or TBlockParameterNode(MethodBase m) or TSynthHashSplatParameterNode(DataFlowCallable c) { isParameterNode(_, c, any(ParameterPosition p | p.isKeyword(_))) @@ -696,7 +696,7 @@ predicate nodeIsHidden(Node n) { or n instanceof SynthSplatParameterElementNode or - n instanceof LambdaSelfParameterNode + n instanceof LambdaSelfReferenceNode or n instanceof CaptureNode } @@ -892,10 +892,10 @@ private module ParameterNodes { * "lambda self" from "normal self", as lambdas may also access outer `self` * variables (through variable capture). */ - class LambdaSelfParameterNode extends ParameterNodeImpl, TLambdaSelfParameterNode { + class LambdaSelfReferenceNode extends ParameterNodeImpl, TLambdaSelfReferenceNode { private Callable callable; - LambdaSelfParameterNode() { this = TLambdaSelfParameterNode(callable) } + LambdaSelfReferenceNode() { this = TLambdaSelfReferenceNode(callable) } final Callable getCallable() { result = callable } @@ -1674,7 +1674,7 @@ predicate expectsContent(Node n, ContentSet c) { } private newtype TDataFlowType = - TLambdaDataFlowType(Callable c) { c = any(LambdaSelfParameterNode n).getCallable() } or + TLambdaDataFlowType(Callable c) { c = any(LambdaSelfReferenceNode n).getCallable() } or TUnknownDataFlowType() class DataFlowType extends TDataFlowType { @@ -1695,14 +1695,14 @@ private predicate mustHaveLambdaType(CfgNodes::ExprCfgNode e, Callable c) { /** Gets the type of `n` used for type pruning. */ DataFlowType getNodeType(Node n) { - result = TLambdaDataFlowType(n.(LambdaSelfParameterNode).getCallable()) + result = TLambdaDataFlowType(n.(LambdaSelfReferenceNode).getCallable()) or exists(Callable c | mustHaveLambdaType(n.asExpr(), c) and result = TLambdaDataFlowType(c) ) or - not n instanceof LambdaSelfParameterNode and + not n instanceof LambdaSelfReferenceNode and not mustHaveLambdaType(n.asExpr(), _) and result = TUnknownDataFlowType() } diff --git a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll index 29bcb32a444a..dcec7524bc23 100644 --- a/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll +++ b/ruby/ql/lib/codeql/ruby/frameworks/Sinatra.qll @@ -285,7 +285,7 @@ module Sinatra { } /** Holds if `n` is a `self` parameter belonging to block `b`. */ - private predicate blockSelfParameterNode(DataFlowPrivate::LambdaSelfParameterNode n, Block b) { + private predicate blockSelfParameterNode(DataFlowPrivate::LambdaSelfReferenceNode n, Block b) { n.getCallable() = b } }